Introdução
Recentemente, conquistei a certificação OSED (OffSec Exploit Developer), focada na exploração de binários através de engenharia reversa, buffer overflow e outros tópicos. A certificação faz parte do curso EXP-301 e aborda diversos assuntos como assembly x86, bypass de proteções (DEP e ASLR), desenvolvimento de shellcodes personalizados, exploração de format strings e etc. Neste artigo, falarei um pouco sobre a experiência e algumas dicas para você tirar um melhor proveito do conteúdo.
Treinamento
Antes de comprar o curso, decidi estudar por fora a fim de começar já com uma base. O treinamento possui 13 módulos, os quais, alguns deles, possuem exercícios e desafios que permitirão que você treine o que foi abordado de forma prática. No meu caso, não realizei todos os desafios, além de nenhum dos 3 challenges. Consumi o treinamento por mais ou menos 1 mês e 10 dias, e, em seguida, fiz o agendamento do exame.
Algumas dicas durante os estudos
- Fazer anotações de tudo o que você aprende (recomendo utilizar o CherryTree ou outra ferramenta similar);
- Fazer anotações dos comandos utilizados no curso;
- Fazer anotações de tudo que provoca erro durante o treinamento;
- Deixe os recursos já prontos para copiar nas anotações, como por exemplo badchars, esqueletos para bypass de DEP, templates de exploit Python, shellcode personalizada e etc.
- Treinar o máximo de extramiles possível. No meu caso, quando iniciei o treinamento, já possuía certa experiência em buffer overflow e engenharia reversa com assembly, então, pulei alguns extramiles.
Artigos que estudei antes de comprar o curso
Exploração normal
https://sghosh2402.medium.com/understanding-exploiting-stack-based-buffer-overflows-acf9b8659cba
SEH Overflow
EGG Hunters
https://medium.com/@orhan_yildirim/egg-hunter-how-it-works-fd0eed671b80
https://fuzzysecurity.com/tutorials/expDev/4.html
ROP Chain BoF
DEP Bypass
https://medium.com/cybersecurityservices/dep-bypass-using-rop-chains-garima-chopra-e8b3361e50ce
https://fluidattacks.com/blog/bypassing-dep
ASLR Bypass
https://codingvision.net/bypassing-aslr-dep-getting-shells-with-pwntools
https://github.com/conduit0x00/OSED_Notes_Public/blob/main/OSED/WinDbg.md
Crash de shellcode
Durante o treinamento, é comum que os estudantes encontrem problemas de crash na hora de executar o shellcode. Após aprender com os erros, decidi mastigá-los e colocar as possíveis razões neste review:
Espaço no buffer esgotado
O espaço no buffer acabou, cortou a sua shellcode e você não percebeu.
Badchars ainda presentes
Ainda falta identificar badchars.
Alinhamento do stack
O valor do registro ESP não é divisível por 4. Certifique-se que o endereço definido no registro ESP seja divisível por 4. Para isso, é possível dividir o valor por 4 e verificar se o resto é 0, se sim, o valor está alinhado.
Comando do WinDbg: ? esp % 4.
NOPs
Recomendo sempre colocar certa quantidade de NOPs, tanto atrás como depois do shellcode. Alguns encoders podem utilizar do espaço acima do shellcode para o registro de dados temporários na funcionalidade de decodificação. Já os NOPs após o shellcode, podem te ajudar a evitar confusão e identificar se o shellcode foi cortado, entre outros erros.
Alinhamento do início do shellcode
Recomendo sempre que o shellcode inicie em um endereço divisível por quatro a fim de evitar erros. Isso pode ser feito através da inserção de NOPs antes do shellcode.
Permissão da memória onde o shellcode se localiza
A região que o shellcode está não possui permissão WRITE. Vale ressaltar que, o msfvenom gera payloads com encoder, os quais removem os badchars através da encodificação, e, em seguida, decodifica na memória.
Comando do WinDbg para identificar permissão do endereço: !vprot <endereço>.
Code cave sem permissões de escrita
Quando você escreve o seu shellcode em um code cave através de alguma metodologia, não há permissões de escrita (WRITE), logo, caso o seu shellcode utilize encoder, não irá funcionar e provocará um crash.
VirtualAlloc no limite de página
Em alguns casos, ao executar o VirtualAlloc (com ROP Chain), é possível que o endereço definido em lpAddress esteja perto do final do limite de página da memória, o que pode fazer com que uma parte da shellcode tenha sido definida com PAGE_EXECUTE_READWRITE e a outra não, provocando, assim, um crash. Quando o crash ocorrer, você pode verificar a permissão de cada linha no depurador. Além disso, é importante colocar um dwSize maior do que apenas 1 (eu recomendaria um tamanho um pouco maior que o dos NOPs + o shellcode).
Proteção GS
Buffer muito grande, o que pode atingir partes protegidas pelo GS e crashar a aplicação.
Assembly de outra arquitetura ou com algum erro
O seu shellcode pode ser de outra arquitetura ou conter erros de programação caso tenha sido personalizado.
Outras razões
Caso você tenha tentado todas essas soluções e mesmo assim o seu shellcode crashou, você poderá perguntar para os outros estudantes no Discord da OffSec. Além disso, fique à vontade para me enviar uma mensagem da razão para eu incluir neste artigo.
Exame
Segundo a documentação pública da OffSec, o exame oferece 47 horas e 15 minutos e possui três desafios, os quais envolvem engenharia reversa, desenvolvimento de exploit e criação de shellcode personalizado. Dos três desafios, você precisará completar no mínimo dois, o que não é tão difícil dentro das 48 horas. Após a finalização do tempo, você terá mais 24 horas para documentar a exploração em um relatório. No meu caso, passei na primeira tentativa.
Algumas dicas para se preparar
- Treinar ROP Chain;
- Treinar engenharia reversa;
- Treinar o desenvolvimento de shellcodes;
- Treinar em binários oficiais e não oficiais;
- Ler diversos reviews disponíveis na internet;
- Ler o FAQ e guias do exame.
Dicas durante o exame
- É recomendado dormir entre 7 e 9 horas. Logo, para não perder tantas horas e ao mesmo tempo ter um sono saudável, eu recomendaria um sono de pelo menos 7 horas. Não durma pouco por ansiedade, já que, isso poderá prejudicar o seu raciocínio lógico e compensar o tempo não dormido;
- Compareça na ferramenta de monitoramento 20 minutos antes;
- Aprenda com o meu erro e evite atrasos: não é permitido Notebook + HDMI. Você deverá utilizar apenas a tela do seu notebook ou um computador desktop;
- Beba bastante água durante o exame;
- Pare para descansar se você ficar preso, a solução virá na cabeça durante o descanso;
- Printe as etapas em cada avanço;
- Não coloque complexidade em alguma etapa, talvez a solução seja mais simples do que parece;
- Não desista, mesmo que falte pouco tempo para finalizar, a solução poderá vir nos últimos instantes. Utilize 100% do tempo que você possui, você obterá experiência;
- Utilize os últimos instantes para verificar se falta coisa para tirar print;
- 48 horas é mais que suficiente para passar.
Algumas instruções assembly para iniciantes
Algumas pessoas podem não entender de primeira o que cada uma das instruções x86 fazem, então aqui vão alguns exemplos para te dar uma base para o início do curso:
- mov <regA>, <regB> — Move o valor do regB para regA (isso mesmo, é ao contrário);
- add <regA>, <regB> — Adiciona o valor de regB em regA. É o mesmo que regA = regA + regB;
- lea <regA>, [<regB>+0x04] — Soma o valor de regB + 0x04 e coloca o resultado em <regA>;
- push <regA> — Colocar o valor de regA no topo do stack (fazendo uma analogia, você colocou mais uma caixa em cima de um empilhamento/torre de caixas);
- pop <regA> — Tirar o valor que está no topo do stack e colocar em regA (tirou a caixa do topo da torre e colocou em uma estante).
Vale ressaltar que, assembly é como se fosse um jogo de manipular uma pilha (tipo colocar caixas uma em cima da outra ou tirar), você faz o que quiser dependendo do seu objetivo. Assembly seria a linguagem para controlar a sua CPU a fim de chegar no seu objetivo, não existe o “correto” ou “errado”, você apenas faz um jogo de “empilhar caixas” para chegar em algum objetivo próprio. O que pode existir são convenções globais opcionais a serem seguidas, tipo utilizar determinado registro (ex.: EAX, ECX, EBX e etc) para determinado papel. Os registros são como se fossem variáveis.
Analogia do empilhamento de caixas
Em um armazém de produtos:
- Mario coloca uma caixa contendo um celular em cima da torre
- João tira a caixa do topo e coloca na estanteA
- Mario coloca uma caixa contendo um computador em cima da torre
- João tira a caixa do topo e coloca na estanteB
Em assembly ficaria assim:
- push caixa_celular
- pop estanteA
- push caixa_computador
- pop estanteB
Alguns recursos de brinde
Template exploit Python TCP
#!/usr/bin/python
import socket
import sys
import struct
try:
server = sys.argv[1]
port = 80
buffer = b"A" * 2000
print("Sending evil buffer...")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(buffer)
print(s.recv(1024)
s.close()
print("Done!")
except socket.error:
print("Could not connect!")
Template exploit Python HTTP
#!/usr/bin/python
import socket
import sys
import struct
try:
server = sys.argv[1]
port = 80
inputBuffer = b"A" * 2000
content = b"username=" + inputBuffer + b"&password=A"
buffer = b"POST /login HTTP/1.1\r\n"
buffer += b"Host: " + server.encode() + b"\r\n"
buffer += b"Content-Type: application/x-www-form-urlencoded\r\n"
buffer += b"Content-Length: "+ str(len(content)).encode() + b"\r\n"
buffer += b"\r\n"
buffer += content
print("Sending evil buffer...")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(buffer)
print(s.recv(1024)
s.close()
print("Done!")
except socket.error:
print("Could not connect!")
Badchars
badchars = (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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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") # 255 Caracteres
Binários para treinar
- https://github.com/bmdyy/quote_db/
- https://github.com/bmdyy/signatus
- https://github.com/xct/vulnbins
- https://github.com/stephenbradshaw/vulnserver
- https://github.com/vvinoth/SatraAcademy
- https://github.com/bmdyy
- https://github.com/Sh3lldon/RemoteApp/tree/main/RemoteApps
Cheatsheets
- https://github.com/nop-tech/OSED
- https://github.com/epi052/osed-scripts/
- https://karol-mazurek.medium.com/osed-tips-exp-dev-x86-d00c4a5ca006
Referências
- https://help.offsec.com/hc/en-us/articles/360052977212-EXP-301-Windows-User-Mode-Exploit-Development-OSED-Exam-Guide. OffSec
- https://www.offsec.com/courses/exp-301/. OffSec
Good exam!