callme
Calling functions with arguments, in the right order.
The challenge description provides some good insight into what we will be doing here:
You must call the
callme_one(),callme_two()andcallme_three()functions in that order, each with the arguments0xdeadbeefdeadbeef,0xcafebabecafebabe,0xd00df00dd00df00de.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.
This makes stepping with gdb monumentally more difficult. Because the function is not fully in the binary, gdb often gets confused. The main way to fix this in our exploit file is to do the following:
elf = context.binary = ELF("./callme")
libc = ELF("./libcallme.so")This way, the custom library file is loaded correctly with the functions. Then, when we define the process using gdb.debug, it correctly loads in the library file.
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.
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?