Yesterday, sickn3ss (one of the frequent visitors of the #corelan channel on freenode IRC) posted a really interesting question.

The question

While testing ROP gadgets, as part of the process of building a DEP bypass exploit for WM Downloader, he wanted to know if there is a way to predict the required padding needed to properly align/set up the stack, when a gadget is used that ends with RET + offset.

Apparently a lot of people assumed that the offset (offset to RET) needs be compensated immediately after the gadget pointer… but that’s not true.

Let’s visualize the issue & see if we can find a general rule.

Let’s say your ROP chain contains the following 3 (fake) gadgets :

77C1E842 : PUSH EDI / POP EAX / POP EBP / RET

77C1D7F5: ADD EAX,20 / POP EBP / RET

71AA2526 : XOR EAX,EAX / INC ESI / RET

…

When setting up the stack with these pointers, the exploit developer has to compensate for any data that will be picked up (POP) or otherwise has to be put on the stack in order to make sure the RET (at the end of the gadget) will return to the next gadget pointer.

The example setup (using the pointers above) would look like this :

ESP 77C1E842 ESP+4 DAC0FF33 ESP+8 77C1D7F5 ESP+C DAC0FF33 ESP+10 71AA2526 ESP+14

Nothing really special here.

What happens if the gadgets end with RET + offset ? How does that impact the stack alignment / padding bytes you need to put in between 2 gadget pointers ?

The answer

Let’s say we have the following gadgets :

77C1E842 : PUSH EDI / POP EAX / POP EBP / RETN + 4

77C1D7F5: ADD EAX,20 / POP EBP / RETN + 8

71AA2526 : XOR EAX,EAX / INC ESI / RET

…

The first gadget ends with RETN+4. The second gadget ends with RETN+8. How does this impact the stack layout ?

ESP 77C1E842 ESP+4 DAC0FF33 ESP+8 77C1D7F5 ESP+C 41414141 ESP+10 DAC0FF33 ESP+14 71AA2526 ESP+18 41414141 4 bytes of padding – compensate for the first 4 bytes in RET+8 (gadget 2) ESP+1C 41414141 4 bytes of padding – compensate for the second 4 bytes in RET + 8 (gadget 2) ESP+20

Conclusion : the offset to RET must be accounted for on the stack after the next gadget pointer, and not after the current gadget pointer.

Thanks sickn3ss for popping the question & working with me to documenting the behaviour !

Copyright secured by Digiprove © 2011 Peter Van Eeckhoutte

© 2011, Corelan Team (corelanc0d3r). All rights reserved.

Related Posts: