For a long time I’ve been casually wondering about how ROP really works and what it is. I understand buffer overflows and shellcode in stacks and heaps, but a simple explanation to ROP was apparently hard to find. I was reading about something else when it hit me how this concept works, but further reading was still a bit hazy to me.
While it is a powerful way to avoid Data Execution Prevention (when CPU will not execute code in heap and stack), it a really simple concept.
In simple terms, a buffer overflow on the stack introduces a return address and then some shellcode the return address should goes to. Data Execution Prevention (DEP) prevents the shellcode from being executable in the stack (and the heap), so that is a no-go in modern computing. When doing ROP, instead of introducing shellcode, you just introduce a whole bunch of return addresses pointing near the bottom of subroutines already established in the program.
Instead of creating shellcode, just find a whole bunch of subroutines that contain the instructions you want near the bottom of it. So if you want to execute this code:
mov eax, 0x4 add eax, 0x4 mov ebx, eax
Just find two or three subroutines ending with one or more of these instructions, then smash the stack with multiple pointers to those addresses in memory. The first one being something like:
mov eax, 0x4 ret
Then a second that might be
add eax, 0x4 mov ebx, eax ret
And then you’ve got the
ebx register stored with 0x8.
It is, of course, way easier to just introduce your own shellcode, but when it is not an option, this is still easy enough to pull off. ROP has been used in the past as a stepping stone to disable DEP and then go on to use shellcode, but with the introduction of hardware DEP I’m not sure this is as feasible anymore.