Prerequisite
Reading: previous “Stack Pivoting” article
Cyber security seems to be an
arms race between attackers and defenders (in addition to the arms race between nations). Every time defenders devise a new
mechanism to defend computers and mitigate exploits, attackers seem to find a
way around it. Such was the case with DEP (Data Execution Prevention). Defenders
used this mechanism to prevent execution from regions of memory that were supposed
to contain data only rather than code. This was supposed to prevent attackers
from executing shellcode from memory structures such as the program stack or
the heap. To bypass DEP, the ROP exploitation technique was devised. It is similar to the
idea of Ret2LibC [1].
ROP works by taking advantage of
the fact that the attacker can manipulate program execution control data. In this
technique, the attacker injects a fake call stack and executes a “stack pivot” (see
prerequisite reading) to pivot to it. The call stack can be thought of as recording
the causality chain [2] that specifies how execution got to its current
position (ie which functions called which functions in order to get to the
current function). When returning from the current function, the normal call stack
serves the purpose of controlling where the execution will return to. For
example with the normal stack, the immediate return address is supposed to be in
the function that directly called the currently executing function. Rather than
pointing in the functions that are part of the current causality chain, each return
address in the fake call stack points to what is known as a “ROP Gadget”.
A ROP Gadget is any “useful instruction(s)”
to an attacker followed by a return instruction. The instruction(s) that are
considered “useful” depend on the vulnerability the attacker is trying to
exploit. The return instruction gets the next value off of the attacker controlled
call stack, which in turn points to the next ROP Gadget to be executed. One
crucial property of a ROP gadget is that it must be at a predictable address in
memory every time the vulnerable program is executed, so the fake call stack
can accurately point to the intended ROP gadgets. A stack pivot gadget is a subset
of the more general ROP Gadget in that the useful instruction(s) of a stack
pivot gadget switches the value of the ESP register from the real stack to the
fake stack. Examples of the stack pivot instructions are given in the
prerequisite reading. Here are some examples of general ROP gadgets:
- push EAX
ret - pop ECX
ret
- sub EAX, 4
ret - pop EBX
xor EAX, EAX
ret - add ECX, 8
ret
ROP exploits work because the attacker
tricks the machine into using attacker controlled program control data which is
injected into a page that’s not necessarily executable. This technically is
allowed even with DEP enabled because the fake call stack bytes injected by the
attacker are technically not being executed. Rather, they are just controlling
where execution of the CPU will go next. In some sense, the attacker is
actually turning the process’s address space against itself by using instructions
already present in executable sections, but just executing them in different
orders to achieve the attacker’s intentions. Often the end goal of ROP exploits
is to make executable a currently non-executable region in memory, so that
shellcode that has been injected into that region can be executed. This requires
a return address on the fake call stack to contain a pointer to a function
(such as VirtualProtect) along with the required parameters.
The execution of a ROP exploit
looks similar to the following once the fake call stack is injected in memory:
- Execute stack pivot gadget to pass control to the fake call stack
- Execute a ROP Gadget at the top of the fake callstack
- Execute “useful instruction(s)"
- Execute a return instruction
- If the next return address is another ROP gadget, goes back to step 2.1
- Else if the next return address is a function, executes that function using parameters that are on the fake stack
Above, step 2.2.1 is implicitly
“goto step 2.1” if the return address points to another ROP gadget. This forms
a repetitive chain, also known as a “ROP Chain”. A
function can be executed if the fake call stack contains the function address
and the required parameters (step 2.2.2).
An example of a ROP exploit follows.
A local variable buffer on the stack has already been overflowed and the return
address of the current stack frame has been overwritten with the address of the
below stack pivot gadget. That function has already returned to the stack pivot
gadget below and the stack pivot instruction below has already been executed.
Stack Pivot Gadget:
c3 ret //EIP points here. This is the next instruction to be executed
Fake Call Stack:
Fake Call stack right before "ret" instruction of the stack pivot gadget is executed |
Defenders created DEP to stop
shellcode execution from data-only regions of memory. Attackers created ROP to
bypass DEP. Then, Defenders created ASLR to stop ROP exploits. And so the cyber
security arms race goes on and on…
References: