Ret2win
Arquivos
ret2win
Elf compilado com a flag --no-pie
main.c
Código fonte do elf.
solve.py
Script em python.
📥 Download: Arquivos
O que é Ret2Win
Ret2win é um exploit que envolve a pilha (stack
), de forma que nós reescrevemos os valores armazenados nela para alterarmos o endereço de retorno para uma função win (por isso o nome ret2win).
Mas como isso funciona? Bom, quando chamamos uma função o endereço da próxima instrução é salvo na pilha (rsp
armazena esse endereço), e no início de toda função nós temos uma sequência de código em assembly que são conhecidos como prólogo da função. Eles são o push rbp
que salva na pilha o conteúdo de rbp
, e o mov rbp, rsp
que faz o rbp
ser igual ao rsp
, essas duas instruções basicamente criam um novo segmento de pilha para aquela função. Agora que toda a pilha para a função está criada, ela começa a alocar espaço para as variáveis (se houver) subtraindo o valor de rsp
pelo tamanho da variável (note que a pilha cresce de cima para baixo). Com tudo isso feito, se nós conseguirmos fazer uma variável armazenar um valor maior que o seu tamanho, nós na verdade começaremos a sobrescrever todos os valores que já estavam na pilha, e o objetivo é fazer isso até chegarmos no local onde o endereço da instrução foi armazenado, e assim rescrevemos ele para ser o endereço para a função que queremos ir.
Analisando o código
Normalmente você nunca recebe um arquivo de código para que você simplesmente olhe diretamente para ele e veja as vulnerabilidades, você geralmente vai receber um arquivo executável (na maioria dos casos .ELF). Para nós vermos o código é necessário usar alguma ferramenta que desmonte o executável em seu código assembly, e aqui nós usaremos duas ferramentas: o Ghidra que consegue mostrar o código assembly compilado para pseudo C ou C++, e também permite uma fácil visualização de todo o código, e o PwnDbg que também permite uma visualização do código assembly, mas usaremos ele principalmente por causa da sua capacidade de debugar.
Abrindo o ELF disponibilizado no Ghidra podemos ter acesso a função main
:
Podemos notar que a main
chama outra função, vamos dar uma olhada nela.
Certo, temos uma função que usa o scanf
para armezar o input do usuário em uma variável de tamanho 16 (vale notar que o scanf não tem limite de leitura, esse limite deve ser específicado nos operadores de formato, e nesse caso não há nenhum limitador). Já identificamos a possível vulnerabilidade, mas e a função win? Bom temos que olhar no código para termos certeza que ela existe, para fazermos isso é só usar o Ghidra.
Nota: Normalmente não vai existir uma função win, como neste caso, mas isso será abordado nos próximos exploits.
Segue a função win:
Debugando com PwnDbg
Agora que sabemos todas as funções, vamos começar a debugar:
Solução
Esse é o estado da pilha após ela criar espaço para a nossa variável:
Logo para chegarmos no endereço de retorno, teremos que escrever 24
caracteres (0x18
-> 0x7fffffffddd8
- 0x7fffffffddc0
). Antes de fazermos um script para isso, nós temos que dar uma olhada na função win
para acharmos seu endereço:
Seu endereço é 0x0000000000401146
, vamos para o script:
Esse script é bem simples, nós usamos a biblioteca pwn
que nos permite acessar "programas" por meio do script. Com ela nós abrimos o nosso processo (ret2win), criamos uma mensagem em bytes contendo os 24 caracteres mais o endereço da win (em little endian), e enviamos essa mensagem para o programa. Para pegarmos a mensagem do terminal nós usamos a p.recvuntil(b"?").decode()
, essa função retorna tudo até o caractere ?
, em um formato de array de bytes, e por isso usamos o decode()
para traduzir para nós.
O único problema desse script é que a função recv
da biblioteca pwn
pode gerar erros se ela não encontrar o critério de parada, a outra opção para ela é usar o log.info()
.
Resultado
Atualizado