Return-Oriented Programming (ROP) — Practical Primer
What is ROP?
- Technique that chains short code snippets ("gadgets") already present in a process's memory to perform arbitra
- Commonly used to bypass non-executable stack protections.
Why it matters
- ROP is fundamental for practical exploitation of modern hardened binaries.
- Understanding gadget discovery and mitigation helps both offensive and defensive work.
Core concepts
1. Gadget: short sequence ending with a 'ret' (or similar control transfer).
2. ROP chain: sequence of gadget addresses that set up registers and call functions.
3. Calling convention: needed to set arguments properly (x86 vs x86_64 differences).
Finding gadgets (high level)
- Use binary gadget scanners (ROPgadget, Ropper) to enumerate gadgets.
- Prioritize useful gadgets: pop reg; ret, mov [reg], reg; ret, call [reg].
- Chain smaller gadgets if larger ones not available.
Building an exploit (steps)
1. Identify vulnerability and crash control point.
2. Leak memory addresses if ASLR present (info leak, format string, heap leak).
3. Locate useful modules (libc, program segment) with gadget density.
4. Create ROP chain to call system() or mprotect + shellcode execution.
5. Test iteratively in controlled environment (qemu/gdb).
Defenses & mitigations
- ASLR (address space layout randomization)
- DEP/NX (non-executable memory)
- Stack canaries
- Control-Flow Integrity (CFI) and shadow stacks
- PIE (position independent executables)
Practical checklist for testers
- Check whether binary is PIE/ASLR/NX enabled.
- Use tools to enumerate gadgets; document module base addresses.
- Attempt simple system("/bin/sh") first; fallback to mprotect + shellcode.
- Log each attempt and cleanup environment before retesting.
References & resources (suggested)
- ROPgadget, Ropper, radare2
- Practice in CTFs and vuln labs (use local VMs).
End of primer.