For decades the standard exploit recipe was simple: overflow a buffer, write your own shellcode into memory, and jump to it. Defenders responded with non-executable memory — the operating system marks the stack and heap as data-only, so the CPU refuses to execute anything written there. Problem solved, right?
Not quite. In 2007 Hovav Shacham showed that you do not need to write new code at all. Every program loads libraries like libc that are already full of executable machine instructions. Buried in those instructions are short sequences ending with a ret (return) instruction — tiny fragments Shacham called gadgets. Each gadget does one small thing: perhaps it loads a register, or adds two numbers, or writes a value to memory.
The trick is chaining: if an attacker can control the stack — say, through a classic buffer overflow — they can plant a list of return addresses. Every time a gadget executes its ret, the CPU pops the next address off the stack and jumps there. The attacker never injects a single new byte of executable code; they just redirect the flow through fragments that were already present.
This attack, Return-Oriented Programming (ROP), completely sidesteps the non-executable-memory defense. It was later proved that ROP is Turing-complete: given a rich enough binary, an attacker can compute anything using only existing fragments. The defense arms race has never quite recovered.
Comments
Loading comments...