Buffer Overflows on x64 with radare2

July 13, 2019
r2 radare2 x64 exploiting rop

The approach to exploit buffer overflows on x64 is a bit different that on x86. This post demonstrates this using the split challenge of ROP Emporium while making use of radare2.

RIP & Canonical Addresses

The first thing one notices when trying to gain control over the instruction pointer is that only values of a specific range are allowed to be loaded into the RIP register. On x86 arbitrary values can be loaded into the instruction pointer register (EIP) - on x64 only canonical values are allowed. These are values of the following ranges:

Other values are illegal and the program aborts when attempting to load such a value into the instruction pointer register, e.g. after a ret instruction. A valid value for the user space memory region would therefore be 0x0000414141414141 and not 0x4141414141414141. This is important to keep in mind while fuzzing.

Passing Parameters

A second aspect of x64 exploitation is that function parameters are being passed using the RDI, RSI, RDX, RCX, R8D and R9D registers. All remaining parameters are passed using the stack. This is different than the x86 semantics where parameters are being passed using the stack.

This makes x64 exploitation a bit more difficult if you want to call a function that’s already existing because function parameters have to be loaded into the respective registers prior to executing as function in the target process. For this, a very small ROP payload can be used to populate the required registers first and return or jump to the payload afterwards.

To accomplish this, the /R search of r2 can be used, e.g. like this:

r2 split -c "/R pop rdi; ret"
  0x00400883                 5f  pop rdi
  0x00400884                 c3  ret

This is a ROP gadget that can be used to leverage an exploit.

Building The Payload

This gadget can be used with a payload of the following structure:

  1. Padding to cause the overflow, in this case a string of length 40 is required
  2. Address of the gadget (0x00400883 from above): This will be the value of RIP after returning from the vulnerable function in the split binary
  3. Address of the string /bin/cat flag.txt in the split binary. Search for this using e search.in=dbg.maps and / /bin/cat flag.txt.
  4. Address of the PLT entry of the system function that executes an arbitrary shell command. Use aaa and afl for this.

Please note that all values have to be padded to 8 byte values in the payload because we are dealing with x64. You can use p64() of pwntools for this.

Upon reaching the ROP gadget on runtime, the target process will:

  1. pop the first value of the stack into RDI. At the time of executing this, the value will be the address of the desired shell command
  2. ret to the first value of the stack. This will be the address of system

Exploitation

You can use pwntools to exploit the binary or a simple printf with an r2 profile:

$ printf "`python2.7 -c 'import sys; sys.stdout.write("A" * 40)'`\x83\x08\x40\x00\x00\x00\x00\x00\x60\x10\x60\x00\x00\x00\x00\x00\xe0\x05\x40\x00\x00\x00\x00\x00" > /tmp/in

$ cat > /tmp/profile.rr2 << EOF
#!/usr/bin/rarun2
program=./split
stdin=/tmp/in
EOF

$ r2 -e dbg.profile=/tmp/profile.rr2 -d split -c "dc"

This video demonstrate the exploitation process:


Ok bye.

In-Process Fuzzing With Frida

October 24, 2019
frida exploiting fuzzing reverse-engineering

How Not To Suck At r2wars

October 1, 2019
radare2 r2 r2wars

Dynamic Instrumentation: Frida And r2frida For Noobs

September 13, 2019
radare2 r2 frida r2frida ctf reverse-engineering