Return Oriented Programming (ROP)
Background
Traditionally, many exploits rely on buffer overflows:
- A program writes more data than a buffer can hold.
- The attacker overwrites the stack, including the return address.
- Normally, they’d put malicious code (shellcode) in memory, and overwrite the return address so the program jumps to it.
But modern systems use defenses like non-executable stacks (NX/DEP), which prevent injected code from running. That’s where ROP comes in.
The Idea of ROP
Instead of injected new code, ROP reuses existing code already inside the program (or libraries like libc).
- The attacker overwrites the return address with the address of a short instruction sequence already in memory.
- Each sequence ends with a
retinstruction. - These snippets are called gadgets.
By chaining gadgets together (hence “return oriented”), the attacker effectively builds a program out of already-existing code.
Gadgets
A gadget is usually a few assembly instructions that end in ret.
Example (from x86):
pop eax
retThis gadget pops a value off the stack into the eax register, then returns.
By carefully arranging values on the stack, the attacker controls what gadgets run and in what order.
Why it works
- Since the attacker never injects new code, NX/DEP protections don’t stop it.
- They just reuse code that’s already marked executable.
- With enough gadgets, attackers can simulate arbitrary computations (Turing complete!).