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  ./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).

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?