ROP Emporium - pivot
The pivot challenge creates a situation where stack space is limited. This means that our full payload cannot be stored on the stack and instead must be located elsewhere in memory. However in order to start executing the code pointed to from the new stack we have to swap stacks! This is called pivoting and let’s get started.
Exploring the binary⌗
The pivot binary is linked with libpivot.so
:
The pivot
binary contains a uselessFunction
:
The foothold_function
from libpivot.so
looks like this:
And it has a ret2win
function of which it is our goal here to call:
The challenge page also states:
In this challenge you’ll also need to apply what you’ve previously learned about the .plt and .got.plt sections of ELF binaries.
When we run the binary we get a sense of what we’re up against:
The address of where to pivot to changes between runs because of this code in main
:
We allocate an object of the specified size and store its address (malloc()
returns a pointer to the allocated memory) in RDI before calling pwnme()
:
The local variable arg1
(RDI) gets moved to RBP-0x28, the address is loaded into RSI via RAX. This is used as the second argument to the printf()
call where it fills in the value for %p
. Eventually the call to fgets()
will use this address to store what it’s read.
Laying out the gadgets⌗
As the output of pivot
instructs, first we need to send it our second payload which will end up on the stack we’ll pivot to.
Then we need to cause an overflow and pivot to the new stack. The stack address is stored in RSP so a gadget needs to be found which affects this register.
Does the job, but that would require passing r13-r15 too and result in a fairly long ROP chain to achieve only setting RSP:
This would be a fairly large chain; let’s see if something shorter is available:
Two useful gadgets with minimal side effects are found:
This is considerably shorter so let’s use this (also because it modifies fewer registers).
One contrived attempt of exploiting this binary was to leak the address of foothold_function
then calculate the real address of ret2win
. However as we leak the address in a first chain and then call main()
again to use the leaked address in a second chain
we’d already have corrupted the stack in such a way that we couldn’t recover it again when going through main()
again.
The key realisation here was to move the GOT pointer into a register because when it’s resolved that’s what the GOT call returns via RAX. So first call foothold_function
to force going through the GOT and have it’s address in libpivot.so
resolved.
Then we pop the address of the GOT entry into RAX. Here comes the trick, by using the “mov rax, qword [rax]” gadget we put the address pointed to by RAX (i.e. the resolved address in libpivot.so
).
We can calculate the offset between the two functions statically:
Now we need a gadget that will allow for an arbitrary addition to RAX:
There are plenty of gadgets to conclude this challenge by calling the function stored in RAX, let’s pick 0x0040098e
.
Exploit⌗
The final exploit is demonstrated here:
It took me a little while to figure out the parsing of the output to grab the flag and ignore the output of the foothold_function()
but recvline_contains()
along with a regex did the trick: