• Nome:Ricardo L0gan
  • Especialidade:Network Security and Security Researcher Enthusiast
  • E-mail:ricardologanbrøgmail·com·br
  • Contato:About-me | Github: Loganbr | Twitter:l0ganbr | Localização:::1
CAPTURE THE FLAG H2HC INVITATION

Nota: Mantive o texto original publicado na Revista H2HC[1] Pag.27


Acredito que este seja um artigo diferente na revista da H2HC, onde pudemos evidenciar que muitos deixam de participar dos Wargames e CTFs por receio de não conseguir resolver os desafios devido à falta de prática. Entretanto, pense: se você não praticar nenhum tipo desses games, quando irá conseguir tal experiência?
Em Infosec, crackme são desafios que envolvem os mais variados tipos de conhecimento de seus participantes, tais como, engenharia reversa, exploração de software, criptografia, análise de tráfego de rede, entre outros.
Para a 12a Edição do H2HC, foi criado um crackme envolvendo criptografia e engenharia reversa, no qual os vencedores ganharam ingressos para o evento [2]. Os interessados podem baixar o desafio em [3].

Ao fazer o download do crackme e executá-lo, temos:

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>wget https://www.h2hc.com.br/desafio/crackme_h2hc2015
v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>./crackme_h2hc2015
Valide a string encontrada:

Passando ao binário qualquer coisa que não seja a chave, a aplicação será encerrada. Também vale destacar que na aplicação ocorre um buffer overflow, porém não será discutido se o mesmo é explorável ou não,pois não é esse o objetivo deste artigo deixamos como exercício aos leitores interessados. Voltando ao crackme, um jogador poderia descobrir a chave caso tivesse conhecimento dos seguintes itens:

• Comandos fundamentais de um debugger (neste artigo, adotaremos o GDB. [4])
• Strings terminam com um null terminator (‘\0’)
• Instrução MOV (arquitetura Intel)

Utilizar do GDB para “bisbilhotar” o que acontece nos internos do crackme seria uma boa ideia, então faremos o famoso “disas main” (disas é um atalho para o comando disassemble, e o “main” é a função principal de diversos aplicativos).

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>gdb –q crackme_h2hc2015
(gdb) disas main]
0x0000000000400c33 <+40>: movb $0x4f,-0x250(%rbp)
0x0000000000400c3a <+47>: movb $0x54,-0x240(%rbp)
0x0000000000400c41 <+54>: movb $0x42,-0x230(%rbp)
0x0000000000400c48 <+61>: movb $0x55,-0x24f(%rbp)
0x0000000000400c4f <+68>: movb $0x48,-0x23f(%rbp)
0x0000000000400c56 <+75>: movb $0x4f,-0x22f(%rbp)
0x0000000000400c5d <+82>: movb $0x54,-0x24e(%rbp)
0x0000000000400c64 <+89>: movb $0x45,-0x23e(%rbp)
0x0000000000400c6b <+96>: movb $0x58,-0x22e(%rbp)
0x0000000000400c72 <+103>: movb $0x0,-0x24d(%rbp)
0x0000000000400c79 <+110>: movb $0x0,-0x23d(%rbp)
0x0000000000400c80 <+117>: movb $0x0,-0x22d(%rbp)
Listagem 1: resultado do “disas main” no gdb - palavra chave em hexadecimal

Perceba que no final dessas movimentações de valores, é notado 3x o $0x0, adivinha quem é? (dica: começa com null e termina com terminator). Seguindo os MOVs, nota-se a formação de 3 palavras: OUT THE BOX, considerando os bytes como símbolos ASCII.

0x0000000000400ce4 <+217>: lea -0x250(%rbp),%rdx
0x0000000000400ceb <+224>: mov -0x258(%rbp),%rax
0x0000000000400cf2 <+231>: mov %rdx,%rsi
0x0000000000400cf5 <+234>: mov %rax,%rdi
0x0000000000400cf8 <+237>: callq 0x400a30
0x0000000000400cfd <+242>: lea -0x240(%rbp),%rdx
0x0000000000400d04 <+249>: mov -0x258(%rbp),%rax
0x0000000000400d0b <+256>: mov %rdx,%rsi
0x0000000000400d0e <+259>: mov %rax,%rdi
0x0000000000400d11 <+262>: callq 0x400a30
0x0000000000400d16 <+267>: lea -0x230(%rbp),%rdx
0x0000000000400d1d <+274>: mov -0x258(%rbp),%rax
0x0000000000400d24 <+281>: mov %rdx,%rsi
0x0000000000400d27 <+284>: mov %rax,%rdi
0x0000000000400d2a <+287>: callq 0x400a30
Listagem 2: Usando strcat para ordenar a string

Contudo, o disassembly da Listagem 2 demonstra a ação da função strcat, concatenando as 3 palavras com os demais, formando-se assim a palavra-chave. Podemos ter uma visualização mais clara ao usar o utilitário “ltrace”, que seria um método mais simples de capturar a string.

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>ltrace ./crackme_h2hc2015
__libc_start_main(0x400c0b, 1, 0x7ffd9c53b1b8, 0x4010c0
strlen(“OUT”) = 3
strlen(“THE”) = 3
strlen(“BOX”) = 3
malloc(10) = 0x228ea10
strcat(“”, “OUT”) = “OUT”
strcat(“OUT”, “THE”) = “OUTTHE”
strcat(“OUTTHE”, “BOX”) = “OUTTHEBOX”
puts(“\nValide a string encontrada:”

Logo, a chave correta é o OUTTHEBOX. Rodando o binário e passando essa chave, temos:

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>./crackme_h2hc2015
Valide a string encontrada
OUTTHEBOX

[!] Verificando string validada...
OK! Agora, novo challenge. Escreva uma função em qualquer linguagem que descriptografe-a e receba um prêmio no final!

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>ls -al read*
-rw-rw-r-- 1 v3n0m v3n0m 450 Set 22 11:41 readme

Perceba que será “criado” o arquivo readme no diretório corrente. O que de fato ocorre é que o binário, ao receber a chave correta, baixa o arquivo readme de um servidor na Internet. A Listagem 3 ilustra esse cenário. No endereço 0x400f93 (Listagem 3) temos o valor “readme” organizado de acordo como a memória funciona [5]e logo em seguida a chamada de inicialização do curl para tratar o destino onde serão colocados os dados lidos.

0x0000000000400f83 <+360>: jne 0x400f5d
0x0000000000400f85 <+362>: lea -0x1040(%rbp),%rax
0x0000000000400f8c <+369>: mov %rax,-0x1058(%rbp)
0x0000000000400f93 <+376>: movabs $0x656d64616572,%rax
0x0000000000400f9d <+386>: mov %rax,-0x1010(%rbp)
0x0000000000400fa4 <+393>: lea -0x1008(%rbp),%rdx
0x0000000000400fab <+400>: mov $0x0,%eax
0x0000000000400fb0 <+405>: mov $0x1ff,%ecx
0x0000000000400fb5 <+410>: mov %rdx,%rdi
0x0000000000400fb8 <+413>: rep stos %rax,%es:(%rdi)
0x0000000000400fbb <+416>: callq 0x4009a0

Listagem 3: Preparando o arquivo readme e iniciando o curl para obter os dados (sH0wNoM3rcY) Para ilustrar, a Imagem 1 mostra a captura do tráfego de rede gerado:
Vamos agora passar para a última parte do desafio, que é a análise do arquivo readme. Primeiramente, obtemos algumas informações gerais:
v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>cat readme

~~~o~~~~~o~~~~o~~~~o~~~~E~~~~~~o~~~~~o~~~~o~~~~~o~~~~E~~~~~o~~~o~~~~o~~
~o~~~~~E~~~~~o~~~~o~~~~o~~~~~o~~~~~E~~~~~o~~~~~o~~~~~~o~~~~o~~~~~E~~~~~~o~
~~~o~~~~o~~~~~o~~~~~~E~~~o~~~~~~o~~~~o~~~~o~~~~E~~~o~~~~o~~~~o~~~o~~~~~~E~
~~o~~~~~o~~~~o~~~~~o~~~E~~~~o~~~~o~~~~~o~~~o~~~~E

Como observado, o conteúdo do arquivo readme não está legível, o que faz sentido dado que o binário pede explicitamente para inserir uma chave. Adicionalmente, ressalta-se que o conteúdo do arquivo não possui assinaturas “manjadas” de algoritmos como BASE64 pois, do contrário, essa parte do desafio não teria graça.

Para revelar o conteúdo do arquivo readme, seria necessário utilizar a operação XOR byte a byte do arquivo com as letras da chave: OUTTHEBOX. Tal operação pode ser utilizada como forma de criptografia simétrica devido à seguinte prioridade do XOR:

dado_origem XOR chave = dado_criptografado
dado_criptografado XOR chave = dado_origem

Escrevemos o código [6]para decifrar o arquivo readme utilizando a técnica supracitada. Vamos agora executá-lo:

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>ls -al read*
-rw-rw-r-- 1 v3n0m v3n0m 450 Set 22 11:41 readme

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>./xor_encryption readme readme_in_plaintext
Chave: OUTTHEBOX
[+] readme is encrypting…
[+] Well done. Your readme_in_plaintext data has been encrypted with successfully.

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>ls -al read*
-rw-rw-r-- 1 v3n0m v3n0m 450 Set 22 11:41 readme
-rw-rw-r-- 1 v3n0m v3n0m 450 Set 22 12:01 readme_in_plaintext

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>cat readme_in_plaintext
01001010 01110110 01110100 01101100 00110101
01111011 01110110 00110101 01001111 00111001
01001111 01001010 00110101 01101000 01110101
01101011 00110101 01001101 01110110 01110011
01110011 01110110 01111110 00110101 01011011
01101111 01101100 00110101 01011110 01101111
01110000 01111011 01101100 00110101 01011001
01101000 01101001 01101001 01110000 01111011
00110100 01001111 00111001 01001111 01001010
01000111 00111001 00110111 00111000 00111100

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>cat decode_crackme_h2hc.py
import binascii
binario = int(‘0100101001110110011101000110110000110101\
0111101101110110001101010100111100111001010011110100101\
0001101010110100001110101011010110011010101001101011101\
1001110011011100110111011001111110001101010101101101101\
1110110110000110101010111100110111101110000011110110110\
1100001101010101100101101000011010010110100101110000011\
1101100110100010011110011100101001111010010100100011100\
111001001101110011100000111100’,2)
print(binascii.unhexlify(‘%x’ % binario))

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>python decode_crackme_h2hc.py
Jvtl5{v5O9OJ5huk5Mvssv~5[ol5^op{l5Yhiip{4O9OJG978<

Continuamos com uma string que não faz sentido. Testar varias combinações ao estilo ROT13 [7], porém com uma soma de 7 ao invés de 13, resolveria o enigma. Desta forma, o desafio estaria totalmente solucionado:

v3n0m@S1th:[/opt/source/crackme_h2hc]─> $>echo $(python decode_crackme_h2hc.py) | perl -pe \ ‘s/(.)/chr(ord($1)-7)/ge’
Come.to.H2HC.and.Follow.The.White.Rabbit-H2HC@2015

By Ricardo L0gan and Leonardo Sena (a.k.a sl4y3r 0wn3r)

Referências:

[1] H2HC 2015 12ed. Acessado em: 16/02/2018.
[2] H2HC - Hackers to Hackers Conference Acessado em: 27/09/2015.
[3] Desafio
[4] GNU Debugger. Acessado em: 27/09/2015.
[5] Extremidade (ordenação). Acessado em: 27/09/2015.
[6] Simulator XOR Encryption
[7] ROT13 Acessado em: 22/09/2015.