A common method for an attacker to control program execution involves creating a “fake stack” using attacker-specified values. When the attacker tricks the victim computer into using a fake stack, the attacker can control the program execution, because the call stack specifies the return behavior of the program from its current state(ie all of the function calls and their corresponding contexts that execution has been through in order to get to the current point). Some important things in the stack that can be fabricated include return addresses, and function arguments.
Return addresses are important because when the computer executes a “ret” instruction, it basically loads the value at the address pointed to by the ESP register into the EIP register, and makes ESP point to one position lower on the stack. Semantically this means that ESP points to the top of the stack, and the computer “popped” the value off the top of the stack into EIP, which is the instruction pointer register. Another way to say this is that execution “returned” to the address stored at the top of the call stack. This fact is crucial to the way ROP exploits work.
Function arguments are also stored on the stack. For example the C code:
myFunction(a, b, c, d); translates to the following assembly code: push d push c push b push a call AddressOfmyFunction
and the stack right before the call instruction is executed looks like:
|…||Lower Memory Addresses|
|Value of a||<-ESP points here|
|Value of b|
|Value of c|
|Value of d|
|Previous stack frames||Higher Memory Addresses|
If we can specify a fake stack, it’s easy to see how we can also control function arguments. Basically, we can fabricate a fake stack to make it look like we pushed values other than d, c, b, and a onto the stack, because every push operation just modifies the stack by making ESP point to 4 bytes higher on the stack, and storing the value we pushed at that location. Fake stacks are extremely useful in ROP exploits, because if we can get execution to occur at a different location with this stack state, we can possibly use these arguments for a function other than myFunction.
On the x86 architecture, a stack is basically a set of DWORDS(32 bit values) in sequence, so we create our fake stack in the address space of a program any place where we can get our own sequence of bytes into the address space. This include buffer overflows, and heap sprays.
Now the difficult part.
If we are using a heap spray, once we have injected our own sequence of bytes representing a fake stack into the address space of the target process, we need to trick the computer into believing that our fake stack should be treated as the real stack. This is a stack pivot, because we are pivoting from the real stack to the fake stack. The basic goal here is to get a value of our choosing into ESP. Any creative attacker can think of many ways to do this. Here are a few possible ways:
- xchg registerContainingFakeStackAddress, ESP
- add ESP, SomeConstant //we can execute this multiple times to get our desired value
- sub ESP, SomeConstant //we can execute this multiple times to get our desired value
- mov ESP, registerContainingFakeStackAddress
- hack the function prologue because they modify ESP there
- hack the function epilogue because they modify ESP there
The trick here is to be creative and figure out how any(or any sequence) of instructions can be used to get our desired value into ESP.
By: Neil Sikka