Exame OSED — Jornada e Dicas

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

https://www.ired.team/offensive-security/code-injection-process-injection/binary-exploitation/seh-based-buffer-overflow

EGG Hunters

https://medium.com/@orhan_yildirim/egg-hunter-how-it-works-fd0eed671b80

https://fuzzysecurity.com/tutorials/expDev/4.html

ROP Chain BoF

https://www.ired.team/offensive-security/code-injection-process-injection/binary-exploitation/rop-chaining-return-oriented-programming

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

Cheatsheets

Referências

Good exam!

Leave a Reply

Your email address will not be published. Required fields are marked *

Copyright © 2024, Decripto.