[ Inicio ] [ Hacking ] [ CTFs ] [ Rant ]
.:: Brenn0 Weblog ::.

Título : Level 1 - Execução de código de variáveis de ambiente - [OverTheWire CTF – Narnia]
Autor : brennords
Data : 13/12/2016
            

Levei mais tempo do que deveria para resolver esse chall e isso apenas prova o que é ter problemas em níveis fundamentais do estudo de exploitation. Fiquei feliz por ter demorando tempo suficiente para aprender novos conceitos e ter estudado uma boa quantidade de assuntos.

Depois da putaria de se justificar para ninguém, hora de write-up o/

Feita a conexão ssh com o servidor narnia do Over The Wire usando o usuário narnia1 e o pass encontrado como a flag do último nível, fui em /narnia e executei o chall da vez:

narnia1@melinda:/narnia$ ./narnia1
Give me something to execute at the env-variable EGG

Hum, ele deseja que se coloque algo na varíavel de ambiente EGG para que possa executar. Vamos tentar e ver no que vai dar.

narnia1@melinda:/narnia$ EGG=BBBB
narnia1@melinda:/narnia$ export EGG
narnia1@melinda:/narnia$ ./narnia1
Trying to execute EGG!
Segmentation fault

Ops, quebramos algo.

Sem perder tempo, hora de um cat narnia1.c e descobrir o que ele quer fazer com EGG.

É bastante simples. No ínicio da função main (linha 4) é declarado um ponteiro de função(1, 2) chamado ret. A sacada aqui é observar como, na linha 12, ret recebe o valor de EGG graças a função getenv, mas não o valor literal de EGG, e sim o endereço que aponta para o ínicio do valor guardado por EGG.

Tudo isso só quer dizer que na linha 13 de main quando ret é chamada, ela irá pular para o ínicio das possíveis instruções que estarão na variável de ambiente. Por isso que o processo vai cuspir um segmentation fault se encontrar quatro B’s.

Me confundi com esse simples comportamento e levei um tempo escrevendo, debuggando e testando código. Vou abrir um parentêses gigante para falar de como entendi:

O código final que me ajudou a entender sobre o chall foi este:

Dentro de uma pasta no próprio /tmp do servidor, compilei meu código (passando argumento para o gcc compilar uma versão de 32 bits porque o pc em que o ctf está rodando tem 64) e criei e exportei a variável de ambiente.

narnia1@melinda:/tmp/1212$ gcc -m32 teste.c -o teste
narnia1@melinda:/narnia$ EGG=BLABLA
narnia1@melinda:/narnia$ export EGG

Depois disso, foi hora de ir no gdb e analisar.

Resolvi omitir o disas da função main (linha 4) para não ocupar espaço no post de forma desnecessária. É relevante só dizer que na linha 5 coloquei um breakpoint na última linha da função (main+201).

Logo executei meu programa e vi ele imprimindo os valores que mandei.

Na primeira linha da execução, ele imprime o valor armazenado em EGG no formato de string (“BLABLA”). Na segunda, o ponteiro para EGG (0xffffd8a0). Na terceira, no formato inteiro (-10080). Na quarta linha da execução, imprimi o ponteiro não inicializado de ret e em seguida imprimi o mesmo depois de ele ter ganho o valor de getenv(“EGG”).

Depois de todo o trabalho pesado do programa, o breakpoint entra em ação. Meu objetivo era poder checar e ter certeza do valor que ret receberia e para onde ele apontava.

Usando x/s + o endereço que ret ganhou, pude ver que o BLABLA estava lá.

Após entender o código do chall, ficou fácil ter ideia do que fazer em seguida: enriquecer EGG com instruções válidas, de preferência instruções que dessem uma shell! Ou seja, um shellcode!

Assim como no chall anterior, usarei o python para passar as instruções em um formato que o processo entenda e execute.

Claro que poderia copiar algum shellcode pronto e tentar fazer funcionar com o chall, mas preferi ir atrás de mais conhecimento e acabei encontrando um ótimo paper ensinando a construir shellcodes: https://www.exploit-db.com/papers/13224/ (aqui tem um outro que está em pt-br, só dei uma lida por cima mas parece ser bem equivalente ao outro).

Com a ajuda do paper, criei shellcodes que não fazem nada, só chamam exit com o código de retorno 0:

narnia1@melinda:/narnia$ EGG=`python -c 'print \x31\xc0\xb0\x01\x31\xdb\xcd\x80'`
narnia1@melinda:/narnia$ export EGG
narnia1@melinda:/narnia$ ./narnia1
Trying to execute EGG!
narnia1@melinda:/narnia$ echo $?
0

Você pode ver que dessa vez não houve segmentation fault.

As crases, no ínicio e no fim do comando, servem para avisar que não quero a string “python -c…” e sim o resultado desse comando armazenado em EGG.

Fiquei brincando um tempo e criei ainda mais shellcodes inúteis:

Ainda pensei em escrever sobre a criação desses belos códigos, mas preferi não reescrever o que já está bem documentado pela internet por gente que sabe mais do que eu. Me refiro aos links que deixei lá em cima. Não deixe de passar por eles. Seria muito kiddie da vossa parte passar o resto da vida só no copy e paste de shellcodes dos outros.

Mas agora é hora de copiar e colar um shellcode pronto que chama /bin/sh e resolver esse chall!

O código assembly referente ao shellcode que usei está no fim do paper que me ensinou a escrever os meus próprios.

Uhul.