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:
0x0000000000000000to0x00007FFFFFFFFFFF(user space)0xFFFF800000000000to0xFFFFFFFFFFFFFFFF(the other thing)
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:
- Padding to cause the overflow, in this case a string of length 40 is required
- Address of the gadget (
0x00400883from above): This will be the value ofRIPafterreturning from the vulnerable function in thesplitbinary - Address of the string
/bin/cat flag.txtin thesplitbinary. Search for this usinge search.in=dbg.mapsand/ /bin/cat flag.txt. - Address of the PLT entry of the
systemfunction that executes an arbitrary shell command. Useaaaandaflfor 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:
popthe first value of the stack intoRDI. At the time of executing this, the value will be the address of the desired shell commandretto the first value of the stack. This will be the address ofsystem
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 demonstrates the exploitation process:
Ok bye.