RubyOfSec 2.0 (0x177)
71 subscribers
21 photos
12 links
neste canal irei explicar alguns conceitos envolvendo reversing e linux (unix-like tmb). Tmb irei me aprofundar nas provas de conceito de alguns exploits e flaws
Download Telegram
entao basicamente o que podemos fazer eh modificar o opcode pra outra instruçao tipo nop-slide ou coisa do tipo, nesse caso podemos deixar o prefixo e simplesmente deixar o tamanho do salto igual a 1
ficaria e900000000
e agr ao executar atingiriamos o fluxo de execuçao que queriamos, assim obtendo a flag

HTB{y0U_4R3_a_g0lf3r}
isso pode ser feito com qlqr editor hexa,basta encontrar o byte que queriamos modificar


Bem, eh um ctf bem tranquilo e facil, porem achei mto interessante pra explicar como funciona o patching de binarios

O que fizemos aqui foi identificar a instrução que queriamos modificar, analisar os opcodes para modificar arbitrariamente em linguagem de maquina (usando um editor hexa) e desviar ao fluxo para encontrar a flag, todo sem modificar durante a depuraçao


Isso pode ser util em alguns casos e eh interessante conhecer o patching diretamente, existem ferramentas que fazem isso, mas ao saber como funciona, podemos desenvolver as nossas ou corrigir erros em ferramentas, isso eh bem mais interessante que soh usar uma interface pronta, dessa forma realmente entendemos
👏2
Encryption Bot - HackTheBox

Vamos com mais um ctf do HTB, dessa vez sera o Encryption Bot, esse chamou-me um pouco a atenção jah que em relação a outros challenges do nível facil-medio a media de usuários que obtiveram a flag eh bem pequena, vamos descobrir o porque


Primeira fase: Estudando o comportamento (entrada e resposta) e Analise com
r2 e gdb

nem preciso explicar muitas coisas sobre o binario, como jah falei varias vezes aqui, meu foco eh unix/linux, entao qndo extraimos o .zip fornecido pelo HTB obtemos um elf (x64) e um arquivo ascii flag.enc

- o arquivo flag.enc possui 36 bytes, equivalente a string "9W8TLp4k7t0vJW7n3VvMCpWq9WzT3C8pZ9Wz"

e ao executar o elf (com o nome chall) vemos o seguinte
ao inserir qualquer sequencia de caracteres obtemos a seguinte resposta

I'm encrypt only specific length of character.
(-_-) Find it (-_-)

então ja sabemos o primeiro que temos que fazer na nossa checklist (encontrar o tamanho da string que o binario ira receber como input ate gerar uma resposta diferente) e tmb seria interessante encontrar a relação do arquivo flag.enc com o binario


Analise com r2

bem, ao abrir o binario com r2 (e usar "aaaa" pra gerar uma analise automatica completa) pra analise estatica vemos a primeira característica, diferente do exatlon (outro desafio do HTB que usava UPX) no chall nao foi usado nenhum packer (*basicamente um packer eh uma ferramente bastante usada pra ofuscamento que comprime a seção de .data do binario (assim reduzindo o tamanho dele) e insere uma rotina de descompressao no começo do binario, assim descomprimindo ele unicamente durante a execuçao e alterando seu entrypoint*)
bem, então vamos ao main do binario e vamos analisar seu code
e aqui encontramos a rotina principal e suas chamadas a diversas funçoes, basicamente fcn.00001628 vai mostrar no terminal aquela mensagem ascii contendo (Encryption Bot), dps uma chamada a printf com a mensagem Enter the text to encrypt : como argumento e uma scanf com nosso input que sera armazenado na *var_30h*
Dps temos a criação de um arquivo com o nome data.dat (agora temos algo interessante a explorar) no modo read (no meio disso ha um pequeno salto condicional com JE, que verifica se o arquivo jah existia, se jah existisse algum arquivo com esse nome, passa a deletar ele usando uma chama system com o argumento rm data.dat) e dps passa a escrever um salto de linea ( \n | 0xa em hexa) com putchar

Sinceiramente nada muito interessante por aqui, fora a criação do arquivo data.dat, porem, agr temos nossa primeira chamada a uma funçao interna (fcn.000015dc), o argumento passado a essa funçao eh a *var_30h* (ou seja, nosso input)
int fcn.000015dc (var_30h)
e aqui temos a resposta a nosso primeiro elemento da checklist, basicamente aqui nosso input eh passado pra a library strlen e caso seja menor que 0x1b (27), ira nos retornar aquela mensagem de erro, entao vamos executar novamente e inserir 27 caracteres e observar a resposta
input com 27 caracteres
a resposta eh uma mensagem criptografa com 36 caracteres, equivalente a nosso input, com essa resposta podemos observar algumas coisas, como por exemplo, a saida eh igual ao tamanho da flag.enc, entao ja sabemos o que deve ser feito pra obter nossa flag, entrar com um input que gere de forma equivalente o conteúdo da flag.enc
Uma resposta possivel seria usar alguma tecnica de bruteforce como a execução simbólica, mas pra isso precisamos encontrar o algoritmo de cifrado dentro do binario e o comportamento da entrada

- - -

ateh agr encontramos a quantidade de caracteres do input necessário pra nao receber um erro, agr vamos continuar nossa analise, jah sabemos que devemos procurar um comportamento parecido a um algoritmo da criptografia ou alguma chamada relativa

voltando pra main no radare2 temos que dps da função fcn.00001628 (que verifica o tamanho da entrada observamos que dps de sair da funçao, o 0 eh movido pra rax e ha outra chamada a fcn.0000128a (nada relevante nela, eh soh um alinhamento da stack), dps na main podemos ver que variavel que armazena nosso input eh usado como argumento pra funçao fcn.0000131d
Eu ateh pensei em colocar ela aqui e explicar passo a passo como tenho feito até agr, porem ela eh bastante grande e parece mais complexa do que realmente eh (jah que inclui as optimizaçoes do compilador), mas basicamente vou fazer um pequeno resumo dela aqui

basicamente a funçao pega nosso input (como um vetor armazenado em *var_30h*) e usa um loop para extrair cada elemento do vetor e converte ele em ascii (em hexadecimal, que no nosso caso seria equivalente a "a" = 0x61) e chama uma funçao interna dentro do loop, que converte o elemento hexadecimal a binario ( 0x61 = 01100001) e faz isso ateh o final do vetor e escrevendo a saida no arquivo *data.dat*
assim, o arquivo *data.dat* possui 216 bytes (27 * 8
)

Assim sabemos que o arquivo data.dat eh o equivalente em binario a nosso input, vamos agr pra seguinte funçao fcn.000014ba pra descobrir o que o binario faz com o arquivo
fcn.000014ba
Agr as coisas começaram a ficar interessantes

Antes de continuar, jah que sabemos que precisamos inserir a flag, vamos extrair as strings do binario (podemos fazer isso usando izz com r2 pra extrair as strings globais)
e BOOM, a string HTB{I_4M_R3v3rse_EnG1ne3eR} ali no endereço 0x0000202e. Sera que era soh isso, pq eu enrolei ateh agr analisando o binario se a flag tava o tempo todo como uma string sem ofuscação dentro dele??, bem, vamos testar ela
o resultado criptografado eh diferente da flag real
e como podemos ver, o resultado gerado pela suposta flag eh diferente da flag cifrada que precisamos gerar, ainda assim os primeiros caracteres sao iguais, ou seja, podemos deduzir que a flag real possui HTB{ como primeiros 5 caracteres, o que nao eh nenhuma surpresa sabendo que esse eh o formato das flags usadas pelo HTB. Porem vamos seguir usando esta entrada em nossos testes a fim de analisar o comportamento e encontrar o algoritmo usado pra criptografar o input

- - - -

Bem, voltando pra funçao, podemos ver o seguinte.
A funçao abre o arquivo data.dat e faz um loop onde itera a *var_4* e com fgetc extrai um caractere do arquivo e faz desvios comparativos entre 0 e 1 e dependendo do resultado armazena na stack o 0 ou 1, com cada comparaçao aumenta +1 dentro da *var_4* (equivalente a um (int i = 0; i <= 216; i++) ) onde i eh a *var_4* e passa a executar um bloco de operaçoes (*0x00001541*) com o valor de *var_4* e dependendo do resultado das operaçoes volta pra o inicio do loop fazendo um salto com JNE ou entra pra um pedaço de instruçoes as quais substrai 1 a *var_4* (i) e armazena o resultado em *var_8* (ou seja *var_8* = *var_4* - 1) e entra em outro loop for usando *var_10* com iterador e armazenando 0 dentro de 2 variaveis novas (*var_c*, *var_10*)
Pra continuar precisamos descobrir o que aquele bloco de operaçoes em *0x00001541* faz e da qual o resultado sera usado em *var_4* pra entrar na iteraçao com *var_10*

Para isso vamos usar GDB (com GEF) pra fazer uma analise dinamica e analisar os resultados em stack, vamos abrir o binario com GDB e vamos colocar um breakpoint no endereço equivalente a 0x00001541 e uma vez no breakpoint vamos analisar a stack, nesse caso a *var_4* no r2 eh equivalente a *rbp-0x4* e vamos colocar outro breakpoint no endereço de test antes de entrar no loop iterador (0x00001565)

Uma vez la podemos observar que as operações visam encontrar um numero múltiplo de 6 (eh usado uma multiplicação imul e 2 operaçoes de deslocamento de bits shr e sar, eh bastante interessante conhecer elas) e uma vez atingindo o resultado de igualdade JNE, entra na iteraçao com *var_8*(*rbp-0x8*) = 5 e *var_10* = 0

e no final da iteraçao substraindo -1 o valor de rbp-0x8 e adicionando +1 no rbp-0x10

(int RBP_0x10 = 0; for RBP_0x10 <= 5; RBP_0x10++)

..
..

RBP_0x8 = RBP_0x8 -1