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.
Hi Jayesh. Thanks for reading. Yes, at a higher level of abstraction, a thread call stack is just the basic stack taught in computer science classes.
ReplyDeleteThanks. I started working on my first ROP exploit a few days ago and got it working great maybe 2 days ago. Now my issue is that my primary concern is to try and rewrite it because i had very little room to work with. As soon as I got it working my first goal was to test out setting up virtual protect but I knew I would have to do a second stack pivot for me to have enough space for an actual payload. I thought that maybe there would be enough space at the end of the buffer to get virtualprotect working well enough to flag the last little bit executable and then just put whatever single line of shellcode I felt like to point EIP/ESP to the top of the buffer but then I realized I would have to most likely call virtualprotect a second time once I was where I wanted to be. So right now I'm just trying to figure out how to subtract from ESP and man it's tricky... Currently only really searching one module so I might analyze a couple more to get a wider variety of ROP gadgets. I think I'm on the verge of having a breakthrough I might try a pretty complicated route of using some piece of code that does something like MOV ESP,DWORD PTR [ESP+10] or something like that but I somehow have to figure out what gadgets i can use to store ESP in a location of my choosing where I can then perform a subtraction operation on it and storing it back in ESP but with no ROP gadget to easily do this it's a real puzzle.
ReplyDelete