Este é o writeup dos 5 níveis dos desafios de engenharia reversa do II Hacking N’ Roll, CTF organizado pelo grupo INSERT da UECE.

REV100

Olhando as strings na seção .rodata do binário, que vai de 0x538 até 0xcc8 como mostra o objdump:

$ objdump -j .rodata -x rev100
(...)
Sections:
Idx Name Size VMA LMA File off Algn
 14 .rodata 00000590 08048738 08048738 00000738 2**2
 CONTENTS, ALLOC, LOAD, READONLY, DATA
(...) 

Encontramos uma string interessante(leia: resposta) no editor hexadecimal, como mostra o screenshot a seguir:

REV200

Verificamos que o código em Assembly mostrado não fazia muito sentido, pois acessava a memória de maneira estranha e não parecia corresponder a nenhum algoritmo conhecido.

08048054 <_start>:
 8048054: eb 00 jmp 8048056 <get_call>

08048056 <get_call>:
 8048056: 09 79 20 or %edi,0x20(%ecx)
 8048059: 30 20 xor %ah,(%eax)
 804805b: 75 20 jne 804807d <get_call+0x27>
 804805d: 7c 32 jl 8048091 <get_call+0x3b>
 804805f: 20 20 and %ah,(%eax)
 8048061: 20 6b 20 and %ch,0x20(%ebx)
 8048064: 33 20 xor (%eax),%esp
 8048066: 79 20 jns 8048088 <get_call+0x32>
 8048068: 20 20 and %ah,(%eax)
 804806a: 69 35 20 3a 09 49 27 imul $0x525f6d27,0x49093a20,%esi
 8048071: 6d 5f 52
 8048074: 33 40 6c xor 0x6c(%eax),%eax
 8048077: 6c insb (%dx),%es:(%edi)
 8048078: 59 pop %ecx
 8048079: 5f pop %edi
 804807a: 34 6e xor $0x6e,%al
 804807c: 5f pop %edi
 804807d: 61 popa
 804807e: 73 63 jae 80480e3 <get_call+0x8d>
 8048080: 49 dec %ecx
 8048081: 49 dec %ecx
 8048082: 0a .byte 0xa

Percebe-se a grande quantidade de 0x20(espaço). Utilizando o seguinte código de uma linha em Python:

''.join([a[1].strip() for a in [s.split('\t') for s in open('rev200').xreadlines()] if len(a)==3]).replace(' ','').decode('hex')

Verificamos como era o binário original antes de ser disassemblado, representado pela seguinte string:

“\xeb\x00\ty 0 u 2 k 3 y i5 :\tI’3@llY_4n_ascII\n”

Onde aparece a palavra-chave para o problema ( I’3@llY_4n_ascII ).

REV300

Dentro da função inside2, encontramos uma chamada às funções reveal e scramble, o que indica que a função constrói a chave e depois embaralha com a função scramble.

Então entramos na função scramble e colocamos uma instrução ‘ret’ logo no início da mesma, para que ela não seja executada quando for chamada

.

Salvamos o programa com a modificação acima e executamos, passando as telas, até que ele mostra a chave.

REV400

Verificando os símbolos existentes no binário, encontramos uma função com nome sugestivo – reveal.

Essa função reveal é chamada em apenas um local. Seguindo o xref, achamos a função flag. A função flag, por sua vez, não é utilizada em nenhum local do programa.

Abrimos o gdb, colocamos um breakpoint em main, executamos o programa, e chamamos a função flag usando o comando call do gdb, e verificamos que ela mostra a string desejada.

REV500

Percebemos que várias funções do programa gravavam dados numa região de memória associada ao símbolo ‘hidden’.

Consta no binário que o comprimento do símbolo é de 98 bytes.

Verificamos um ponto na função ‘main’ que estaria depois da chamada de todas as funções.

Executamos o gdb e colocamos um breakpoint nesse ponto. Mandamos executar o programa. Ao chegar no breakpoint, dumpamos a região de memória do símbolo ‘hidden’. Visualizamos uma string cerca de 39 bytes à frente do início do símbolo, e demos um comando print para visualizar melhor. Nessa string aparece a chave.

Infelizmente, nós do grupo CACTUS do IFCE não levamos esta por diversas razões e aproveitamos para congratular os vencedores e organizadores.