callme

Calling functions with arguments, in the right order.

7KB
archive
Open

The challenge description provides some good insight into what we will be doing here:

You must call the callme_one(), callme_two() and callme_three() functions in that order, each with the arguments 0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d e.g. callme_one(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d) to print the flag.

This gives us a good idea of what we need to do. We need to pass these arguments into the correct functions in the correct order.

Notes about the Binary

I skip almost all the static analysis in this binary, mainly because we're provided instruction on exploiting it. However, I want to cover some of the major points that might be confusing.

Padding

The padding is simple; its 40 bytes. It will be the same in all the ROP emporium challenges, but dissecting pwnme will show you why.

The Library File

You probably noticed that we were provided with a libcallme.so file and the binary. We've never been provided with this before, so I want to discuss its importance.

Whenever you build a project in C, it will call on a libc file that contains the standard template library functions. In gdb, we note these functions have a @plt suffix (i.e. puts@plt). However, you can make custom library functions in the same way. In our case, we'll notice in callme that callme_one is referenced as callme_one@plt. This is because callme_one is defined in the library file, and callme only contains a reference to that function.

To dissect callme_one, we need to use gdb (or radare2) on the library file. Then, we can use disas callme_one to get the disassembly of this binary.

Gadget-Hunting

We need to find gadgets that load rdi, rsi, and rdx. We want our gadgets to be as simple as possible, so we'll be restrictive in our gadget search and broaden it as needed.

We'll start by just looking for popping gadgets using the following:

We conveniently find the following gadget, which does everything we need:

This gadget works great! It loads all the values we could need into the registers we need. We'll use this gadget for all three functions.

Important Note:

There is more than one way to do this problem. This happens to be the most efficient, but in the case that gadget wasn't there, there is this combination:

In this case, we would need to load r15 with a random value, but this would work just fine.

While we're at it, we will also grab a gadget to beat the movaps instruction. The longer the payload and the more jumps we make, the more often we need the gadget.

Exploitation

Let's build the exploit from the ground up.

First, we define the binary, library, and the process:

Then, we'll get the addresses of the callme_ functions:

Then, we'll store the values we're going to pass to the functions:

Then, we get our gadget addresses:

Now, we can start building the payload. We need to use the padding to overwrite the return pointer and then perform each jump and call in order.

Finally, we just send off the payload!

Running this gives us the flag!

Full Exploit

Here is the full exploit:

Our exploits are getting exponentially longer in 64-bit as we need to load registers, pass values into those registers, and then call the desired functions. In the next binary, we'll take this a step further to introduce new data into the binary and then use our data to pass our own values.

Last updated

Was this helpful?