gamedev
Descrição do Desafio:
Autor: bliutech Categoria: pwn Descrição:
You've heard of rogue-likes, but have you heard of heap-likes?
Arquivos
chall
Executável.
chall.c
Código-fonte.
Dockerfile
Arquivo docker.
ld-linux-x86-64.so.2
Linker da libc
libc.so.6
libc
solve.py
Script em Python que resolve o desafio.
📥 Download: Arquivos
Passo a Passo da Solução
1. Análise dos arquivos fornecidos
Este desafio fornece tanto o executável como o código-fonte. Analisando o código, vemos que ele é mais complexo, no sentido de não ser apenas 1 ou 2 funções, porém sua temática é bem interessante, já que ele permite o usuário criar 8 níveis de "dungeons", escrever dados em cada um desses níveis, ler o que está nesses níveis, e como qualquer rogue-like, acessar tais níveis.
Para cada nível acessado, as opções são as mesmas, criar, escrever, ler, e acessar.
Analisando a fundo cada função, podemos identificar algumas coisas interessantes:
Temos um vazamento do endereço da
main(), dessa forma já podemos burlar oPIEdo executável.Na função de escrever no nível, nós podemos escrever
0x20bytes a mais do que ele armazena.A função de explorar não faz nenhuma verificação em relação ao endereço, ou seja, podemos acessar qualquer endereço que esteja na índice inserido.
Com isso uma ideia já fica em mente, com o overflow podemos sobrescrever o chunk da frente para que um de seus índices contenha um endereço da tabela .got, e dessa forma, podemos acessar esse endereço, ler o que tem nele para vazar um endereço da libc, e em seguida sobrescrever esse valor para ser a função system(). A questão fica, qual função da tabela .got devemos sobrescrever, por que ainda temos que passar /bin/sh para a função.
E é aí, que olhando para a função get_num(), identificamos nosso alvo, a função atoi() que é chamada como atoi(buf).
2. Exploit
Já sabendo o que fazer, a solução se torna bem simples, só falta descobrir o offset para chegar nos dados (curr->data, se curr for exatamente o endereço da .got, nós não escreveremos nela e nem vazaremos o endereço), e descobrir quantos bytes escrever até chegar no índice do próximo chunk. Com o pwndbg, e o comando vis_heap_chunks, podemos descobrir a quantidade de bytes.
Com isso descobrimos que devemos escrever 48 caracteres, e os próximos começarão a escrever nos índices daquele chunk. Agora, o offset podemos descobrir direto pelo assembly.
Note que antes de chamar a fgets, ele passa como buffer o curr + 0x40, e aí está o nosso offset.
Importante: Como em nenhum momento esse chunk é liberado, não devemos nos preocupar com o programa identificando um erro relacionado ao chunk corrompido.
Importante: O ideal para esse desafio é usar uma ferramenta como o pwninit que cria um novo executável vinculado com a libc informada, pois usando o executável normal e sem o linker, a libc utilizada será a do sistema. No caso, eu não usei o pwninit e por isso tive que abrir a libc separadamente na solução.
Flag
lactf{ro9u3_LIk3_No7_R34LlY_RO9U3_H34P_LIK3_nO7_r34llY_H34P}
Autor da WriteUp
Atualizado