Introduction to Return Oriented Programming

This will be a small introduction into return oriented programming, commonly referred to as ROP. I’ve had a lot of experience with security measures but never really had any hands on experience with more modern security technologies such as non-executable stack/data (DEP, NX, XN, W^X) or Address Space Layout Randomisation (ASLR).

The non-executable stack isn’t really limited to just the stack, anything that isn’t code memory can’t be executed. So as in PSP, you can’t just jump to the savedata and have the day’s work done. No, instead the only code executable is real code provided by the OS when binaries are loaded. This is a nice and funky way but ROP is the counter and tends to poop on this method.

Now, ROP is really cool. Rather than execute your code, you control the stack and execute provided code. Sounds pretty weird, right? I mean, how do you control anything if it’s their code? You’re right in a way, their code isn’t our code. However, we don’t necessarily have to execute code in the same way that they do. I mean, they could have super_duper_function located at address 0x80000000, but just because the function is there, doesn’t mean we have to start our execution there. For example, lets consider super_duper_function does the following:

0x80000000: PUSH {R4-R6,LR}
0x80000004: ... perform algorithm
0x80000050: POP {R4-R6, PC}

First it saves registers onto the stack, then does the algorithm, then restores the saved registers and leaves. Pretty standard stuff here, nothing odd. However, if we control the return address, we can’t execute our code BUT if jump to 0x80000050 then what happens is that more data is loaded from the stack (R4-R6) and the new execution position PC is controlled.

Now, this doesn’t do anything in specific but you can see how this works out. Rather than execute functions as you expect, you essentially build a list of code snippets called gadgets and then you chain them up to perform a task. Lets say we want to do a printf. If we control the entire contents of the stack, we can start off by calling the POP-R4-R6-PC gadget. Suddenly, we control R4, R5, R6 and the program counter.

Consider the next gadget:

0x80000100: PUSH {R4,LR}
0x80000104: .. do stuff
0x80000170: MOV R0, R4
0x80000174: MOV R1, R5
0x80000178: BLX R6
0x8000007C: POP {R4,PC}

Now, we don’t want to execute the whole function, but if we execute to 0x80000170 then what we’re essentially doing is the following:

POP {R4-R6}
MOV R0, R4
MOV R1, R5
BLX R6
POP {R4,PC}

See? we’re chaining gadgets and now we have a function control where we can control the first 2 arguements from the stack without even touching our code. So, first gadget; if we go ahead and set R4 to point to our message, R5 to our optional arguement, R6 to the address of printf and PC to 0x80000170 then we’ve chained together a printf that we control completely without a single drop of our own executable code.

There are a few problems with this though… Gadgets aren’t always easy to find. Especially when it involves specific things like the stack. As ROP code is working its magic, the stack is shrinking away at a variable pace (depending on the gadgets). Now, sometimes you need the address of the stack, variables are loaded from there usually, so if you want to save a return type from something like fopen then you need to store it onto stack (this isn’t the only way to recovery return types though). To store something on stack, you need to construct gadgets in a way that it can safely overwrite data in the future (relative to execution of the ROP stack) so that it is loaded when a POP takes place. Yes, it is a little complicated and there are some gadgets that can ease things.

It’s wise to recognise that your development fun is at the hands of the their compiler and code design. The compiler can do some funny things that is useful, e.g:

0x80000120: MOV R5, R0

loc_0x80000124:
0x80000124: MOV R0, R5
0x80000128: POP {PC}

This gives a lovely control of R0 into R5, a register that we can more easily manipulate (usually). Sometimes, there is just a pain finding the right things to help you. Times like this, it is better to try and use tools to automate the process, but the more complicated gadgets are difficult to find.

Enough about the gadgets, lets talk about architecture. Architecture lends a hand into ROP, mostly when it comes to CISC and RISC. x86 have an absolute baller of a time due to the fact that args are loaded from registers anyway so you just need to go apeshit and call them. RISC is typically a bit different. By using registers extensively, you add another layer where you need to get the correct stuff into the correct registers in order to do the correct thing. This isn’t exclusive, but I see it as more common. However, other than that, the principle of ROP execution is the same, and can be treated exactly the same way.

ROP sounds pretty cool, right? I mean, it poops on execution protection, whatever can the world do? Easy. One of the most effective ways to combat ROP is to incorporate ASLR. I mentioned this earlier but now I’ll gently explain why ASLR bends ROP over. It moves things. There, ROP is countered. ROP requires everything to be static, notice we need to use exact locations in order to even execute anything? Yeah, move it around and ROP doesn’t work. You could say, “Why can’t we just search for it and then dynamically provide a ROP payload??”. Well, you can but good luck finding something that’ll expose such information, since ROP can’t even start, you can’t use ROP to search. Hence the counter to ASLR is either prediction, controlling and/or identifying the locations of memory. If ASLR is used without execution protections, then you can attempt a heap spray to try and get control of data at a significant array of addresses.

So, data execution prevention and ASLR is probably the strongest way to protect a system, but mostly the effectiveness is based upon ASLR. As soon as addresses can be predicted, then huzzah, ROP.

This’ll do for now, If I’ve been a little vague let me know and I can update/repost with better clarification. Since I’m naturally a tease too, I’ll be showing off a cool little project related to this kind of stuff (it isn’t anything amazing, but just a bit of fun!).