I’ve previously written some examples of how to exploit MIPS stack overflows using ROP techniques. The problem is that finding suitable MIPS ROP gadgets manually can be quite tedious, so I have added a new IDA plugin – mipsrop.py – to my github repository.
This plugin searches the code segment(s) of your IDB looking for potentially controllable jump instructions. You can then search the code surrounding these controllable jumps for useful instructions that you might need in your ROP chain.
“Controllable jumps” are defined as jumps whose destination addresses are loaded from the stack, or from other registers (typically during a stack overflow you control several, if not all, of the MIPS subroutine registers, for example).
The plugin’s searches are “dumb” in that they don’t follow code branches, but none-the-less it has proven to be quite effective. As a quick example, let’s look inside a Linux MIPS libc library for ROP gadgets that will let us call sleep(1) in order to force a cache flush.
First, we need to set up the argument to sleep; in MIPS, this means that we need to load the value 1 into register $a0. A typical ROP gadget for accomplishing this might look like:
li $a0, 1 // Set $a0 = 1 move $t9, $s0 // Set $t9 = $s0; we control $s0 via a stack overflow, and can load it with the address of our next ROP gadget jalr $t9 // Jump to the address in $t9 nop
This allows us to get the value 1 into the $a0 register and then jump to the next ROP gadget. After activating the mipsrop.py plugin (Alt+1 hotkey), we can easily search our controllable jumps for the “li $a0, 1” instruction using the mipsrop.find method:
We can see that the first gadget, at offset 0x28BE4, works quite nicely:
With the argument to sleep now set up, our second ROP gadget needs to actually call sleep; but, it also needs to force sleep to return to a location of our choosing (e.g., sleep needs to return to a third ROP gadget). Indirect function returns are ideally suited for this type of operation and generally look something like this:
move $t9, $s2 // Set $t9 = $s2; we control $s2 via a stack overflow, and can load it with the address of sleep() lw $ra, 0x20($sp) // Load the return address off the stack into $ra; since we control data on the stack, we can get the address of the next ROP gadget loaded into $ra jr $t9 // Jump to $t9 addiu $sp, 0x24
Because we control both $s2 and the data on the stack, we can control where this code jumps to and what the return address is. If we load the address of sleep into $s2 during our initial stack overflow, and place the address of our third ROP gadget at $sp+0x20, this gadget will jump to the sleep function, which, upon completion, will return to our third ROP gadget (whatever that may be).
To find an indirect return gadget in our library, we’ll want to search for controllable jumps that move a subroutine register into $t9, then perform a jr instruction:
The gadget at offset 0x2FFD4 loads the return address off the stack and jumps to whatever address is stored in $s2:
Combining this with the first gadget at offset 0x28BE4 gives us a two gadget ROP chain which will call sleep(1), then continue execution from whatever arbitrary address that we place on the stack at $sp+0x24:
loc_28BE4: li $a0, 1 // Set $a0 = 1; this is where we return to after our stack overflow move $t9, $s0 // Set $t9 = $s0; we control $s0 via the initial stack overflow, and can load it with the address of our next ROP gadget at 0x2FFD4 jalr $t9 // Jump to the address in $t9 ($t9 == $s0 == 0x2FFD4) li $a2, 1 ... loc_2FFD4: move $t9, $s2 // Set $t9 = $s2; we control $s2 via the initial stack overflow, and can load it with the address of sleep() lw $ra, 0x24($sp) // Load sleep's return address off the stack into $ra; we control the stack, and can put an arbitrary address here lw $s2, 0x20($sp) lw $s1, 0x1C($sp) lw $s0, 0x18($sp) jr $t9 // Jump to $t9 ($t9 == $s2 == sleep) addiu $sp, 0x28
And that’s pretty much how ROP works in MIPS: find some controllable jalr’s / jr’s that you can chain together to perform a required sequence of instructions while maintaining control of execution.
A few other mipsrop methods you might want to play with are:
- mipsrop.help() – Shows help/usage of mipsrop methods
- mipsrop.system() – Lists all existing calls to system() which point the command string at the stack
- mipsrop.summary() – Prints a summary of all marked positions in the IDB that start with the string “ROP”
There are also some more examples/screenshots in the repository’s README file. Happy ROPing!