Neste exemplo, vou utilizar um binário compilado para x86_x64. Porém, o mesmo exemplo pode ser reproduzido num ambiente x86, basta prestar atenção nos registradores.
Antes de iniciarmos, desabilite o ASLR.
sudo su -
echo 0>/proc/sys/kernel/randomize_va_space
Repare que nosso programa não chama a função letsprint em nenhum momento. Vamos chamar ela atráves da exploração do buffer overflow.
Compilando o binário
Para compilar, vamos utilizar o GCC, caso não o tenha instalado, rode sudo apt install gcc.
gcc -fno-stack-protector -z execstack main.c -o main
Exploração
A exploração é bem simples, só precisamos controlar a execução do registrador RIP. também conhecido como EIP em ambientes x86. o RIP é o registrador responsável por controlar a próxima instrusão que será executada pela CPU.
Vamos utilizar o GDB para analisarmos o binário, caso não o tenha instalado, rode sudo apt install gdb.
gdb -q main
Agora, vamos precisar encontrar a quantidade de bytes necessário para sobreescrever os registradores até chegarmos no RIP. Como não estamos utilizando nenhuma ferramenta de fuzzing, o processo vai ser bem manual, aconselho você começar a partir dos 20 bytes, visto que nosso buffer possui 15 bytes de tamanho.
Com o gdb aberto, rode o comando abaixo.
run `perl -e 'print "A" x 20'`
No meu caso, não foi o suficiente para sobreescrever o RIP, porém, conseguimos sobreescrever o RBP, isso signifca que estamos indo para o caminho certo, vamos aumentar mais alguns bytes.
Novamente, com o gdb aberto, rode o comando abaixo.
run `perl -e 'print "A" x 23'`
Olha que interessante, achamos a quantidade exata de bytes para começar a escrever dados no registrador RIP, veja que ele está apontando para um byte nulo. Agora, podemos colocar qualquer coisa no registrador.
Testando o controle do registrador.
run `perl -e 'print "A" x 23 ; print "B" x 6'`
Veja que agora no RIP aponta para um endereço x4242…que são exatamente os 6 bytes ‘B’ que colocamos no registrador.
Descobrindo o endereço da função
Continuando no GDB, vamos precisar descobrir o endereço da função que vamos colocar no RIP para ser executada.
Utilize o comando abaixo.
disass letsprint
No meu caso, o endereço de memória do início da função letsprint é o 0x000055555555517b.
Em outro terminal, criei um arquivo memory.py e cole o conteúdo abaixo.
Buffer Overflow - Executando uma função que não é chamada na execução do programa.
em Quebra de código
Postado · Editado por 0xdutra
Neste exemplo, vou utilizar um binário compilado para x86_x64. Porém, o mesmo exemplo pode ser reproduzido num ambiente x86, basta prestar atenção nos registradores.
Antes de iniciarmos, desabilite o ASLR.
Código fonte
Crie um arquivo
main.c
com o código abaixo.Repare que nosso programa não chama a função
letsprint
em nenhum momento. Vamos chamar ela atráves da exploração do buffer overflow.Compilando o binário
Para compilar, vamos utilizar o GCC, caso não o tenha instalado, rode
sudo apt install gcc
.Exploração
A exploração é bem simples, só precisamos controlar a execução do registrador
RIP
. também conhecido comoEIP
em ambientes x86. oRIP
é o registrador responsável por controlar a próxima instrusão que será executada pela CPU.Vamos utilizar o GDB para analisarmos o binário, caso não o tenha instalado, rode
sudo apt install gdb
.Agora, vamos precisar encontrar a quantidade de bytes necessário para sobreescrever os registradores até chegarmos no
RIP
. Como não estamos utilizando nenhuma ferramenta de fuzzing, o processo vai ser bem manual, aconselho você começar a partir dos 20 bytes, visto que nosso buffer possui 15 bytes de tamanho.Com o gdb aberto, rode o comando abaixo.
No meu caso, não foi o suficiente para sobreescrever o
RIP
, porém, conseguimos sobreescrever oRBP
, isso signifca que estamos indo para o caminho certo, vamos aumentar mais alguns bytes.Novamente, com o gdb aberto, rode o comando abaixo.
Olha que interessante, achamos a quantidade exata de bytes para começar a escrever dados no registrador
RIP
, veja que ele está apontando para um byte nulo. Agora, podemos colocar qualquer coisa no registrador.Testando o controle do registrador.
Veja que agora no
RIP
aponta para um endereço x4242…que são exatamente os 6 bytes ‘B’ que colocamos no registrador.Descobrindo o endereço da função
Continuando no GDB, vamos precisar descobrir o endereço da função que vamos colocar no
RIP
para ser executada.Utilize o comando abaixo.
disass letsprint
No meu caso, o endereço de memória do início da função
letsprint
é o0x000055555555517b
.Em outro terminal, criei um arquivo
memory.py
e cole o conteúdo abaixo.Lembra-se de trocar o endereço
0x000055555555517b
pelo endereço que você encontrou.Execute o comando
python memory.py
, um arquivo input.txt será criado no diretório que você está.Agora, no gdb, execute o comando abaixo.
Veja que a função
letsprint
foi executada com sucesso. Agora, vamos sair do gdb e executar o programa que compilamos.quit
Agora
Referências