rop2win

Building a ROP chain for a ret2win binary.

3KB
Open

All the challenges in this section are going to be 64-bit challenges. ROP in 32-bit is almost trivial because we pass parameters on the stack rather than the registers, meaning crafting payloads is super simple. In 64-bit, we need to find ways to load the registers with our desired parameters, which is much more difficult.

The challenge is very similar to win64. We need to be mindful that we are in 64-bit.

This challenge, along with all the others, are the 64-bit renditions from ROP emporium. They are by far the best resource for learning how to do ROP.

Static Analysis

The primary functions we see in the binary are:

0x0000000000400697  main
0x00000000004006e8  pwnme
0x0000000000400756  ret2win

ret2win()

This function is straightforward. The string values are statically loaded, and we can easily reverse this function:

void win()
{
    puts("Well done! Here's your flag:");
    system("/bin/cat flag.txt");
}

main()

This function makes a series of back-to-back puts() calls and then calls read_in. In 64-bit, these calls are super easy to interpret, given that you know the parameter register order.

pwnme()

Like the last two, we can pretty easily determine the C code for this function. The only one that's confusing is memset, whose declaration is in the man pages.

Based on what's loaded to the registers, we can tell the function call is memset(rbp-0x20, 0, 0x20):

Let's put the rest of the function together:

If you're struggling to put the C code together, that's okay! This is one of the hardest parts of binary exploitation. It comes with lots of practice! Keep practicing using gdb to dissect binaries. Try making your own, then looking at them to reassemble the code you wrote.

Based on this code, we see a clear buffer overflow vulnerability. We are reading 0x38=56 bytes into the 0x20=32 byte buffer allocated.

Writing the Payload

Like win64, we need to track the following information:

  1. What is the size of the buffer? In our case, it's 0x20 bytes to the base pointer, plus 0x8 bytes for the base pointer, totaling 0x28=40 bytes.

  2. Where are we going? We need to overwrite the return pointer with the address of win(), which is at 0x400756.

  3. Does our input cause the movaps instruction to fail? Our payload is 48 bytes in total, which is divisible by 16, meaning that we should be fine.

Let's write the exploit just like we wrote the win64 one:

Two notes here:

  1. We will consistently read until the > before sending our input because that's how ROP emporium formats their challenges.

  2. We will distinguish between padding and payload (ROP chain) to illustrate the difference.

Most of our exploit files for this section will look the same. We will establish our process, then our critical functions and gadgets, build the padding and ROP chain and then send it to the process.

If we send this off, we'll get our flag!

Last updated

Was this helpful?