Introdução
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 ./protegidoPodemos 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).
gdb -q ./protegido
Reading symbols from ./protegido...
(No debugging symbols found in ./protegido)
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x00001000 _init
0x00001030 strcmp@plt
0x00001040 printf@plt
0x00001050 gets@plt
0x00001060 puts@plt
0x00001070 system@plt
0x00001080 exit@plt
0x00001090 __libc_start_main@plt
0x000010a0 __cxa_finalize@plt
0x000010b0 _start
0x000010f0 __x86.get_pc_thunk.bx
0x00001100 deregister_tm_clones
0x00001140 register_tm_clones
0x00001190 __do_global_dtors_aux
0x000011e0 frame_dummy
0x000011e5 __x86.get_pc_thunk.dx
0x000011e9 verifica
0x00001293 acessa
0x000012d3 main
0x00001330 __libc_csu_init
0x00001390 __libc_csu_fini
0x00001391 __x86.get_pc_thunk.bp
0x00001398 _fini
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.
Last updated
Was this helpful?