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
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:
0xFFFFFFFFFFFFFFFF(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.
A second aspect of x64 exploitation is that function parameters are being passed using the
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 of
returning from the vulnerable function in the
- Address of the string
/bin/cat flag.txtin the
splitbinary. Search for this using
/ /bin/cat flag.txt.
- Address of the PLT entry of the
systemfunction that executes an arbitrary shell command. Use
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
pwntools for this.
Upon reaching the ROP gadget on runtime, the target process will:
popthe first value of the stack into
RDI. At the time of executing this, the value will be the address of the desired shell command
retto the first value of the stack. This will be the address of
You can use
pwntools to exploit the binary or a simple
printf with an
$ 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: