O Buffer Overflow basicamente ocorre quando o programador aloca um determinado tamanho de buffer para uma variável que irá receber uma entrada do usuário e é passado um valor maior do que foi definido, fazendo assim "transbordar" o buffer e sobrescrever outros endereços da memória.
Explorando o binário
Iremos agora analisar um binário para explorar, sem ter acesso ao código fonte.
Para começar, podemos tentar enviar 300 bytes para ver como que o programa se comporta.
$ python3 -c 'print("A" * 300)' | ./protegido
Entre com a senha: Acesso Negado
[1] 1319 done python3 -c 'print("A" * 300)' |
1320 segmentation fault ./protegido
Podemos já perceber que ocorre o "segmentation fault".
Vamos debugar o programa e exibir suas funções com o comando info functions (o parâmetro -q é para o gdb não exibir algumas informações iniciais).
Para colocarmos na sintaxe da intel, basta usar o comando set disassembly-flavor intel.
Precisamos agora rodar o programa com o comando run.
Se quisermos debugar a função main, basta utilizar
Podemos observar que o programa chama a função "verifica".
Com isso, podemos colocar um breakpoint no endereço depois da entrada de dados do usuário, que é a função gets, para analisar melhor. (0x5655621f)
Setando o breakpoint
Agora vamos enviar os dados para o programa utilizando o python
Podemos ver os registradores com o comando i r
Podemos também examinar a memória, olhando em hexadecimal o registrador esp.
Podemos observar que no endereço 0x56556213 da função verifica, é feito uma alocação de um espaço para um buffer de 0x88 que são 136 bytes. Com isso já facilita bastante para sabermos quantos bytes enviar corretamente.
Enviando os bytes
Analisando os registradores, podemos ver que enviamos o suficiente para "quase" atingir o ebp e o eip.
Agora podemos enviar os 4 bytes do ebp e do eip
Com isso, podemos chamar a função acessa (que possui o endereço 0x56556270) que fica dentro da função main. Em seguida podemos analisar a memória e verificar se o EIP está sobrescrito com a função, em seguida só precisamos continuar o programa com sua execução.
Truques no debugger
Se soubermos qual endereço que precisamos ir diretamente, podemos direcionar o programa para isso no debugger.
Se quisermos alterar o fluxo do programa, basta utilizarmos (o endereço 0x56556270 é da função verifica)
Agora, vamos tentar descobrir qual é a senha que o sistema está comparando. Antes da comparação, que é a função verifica, precisamos colocar um breakpoint para analisar a memória e tentarmos descobrir qual é a string que está sendo feito a comparação. Iremos utilizar o endereço 0x5655626e que fica antes da função verifica.