Aller au contenu principal
own2pwn
pwn/pwn-hcfa.tsx

Casser la heap pour détourner un binaire

Index des techniques de heap exploitation ciblant ptmalloc2 et tcache : fastbin dup, unlink, houses of spirit/lore/force/einherjar/orange/roman, chunk overlapping et tout le catalogue tcache.

Maxime Jérôme··6 min de lecture

Prérequis

Hello ! o/

On va voir les différentes attaques de contournement de flow sur la heap. Je vais essayer de vous fournir un max de schémas car c'est vraiment chiant à comprendre quand on est seul à lire how2heap.

Structure de cet article
Cet article est un index. Chaque section correspond à une technique. Les sections sans contenu sont des stubs : la série sera complétée progressivement. Le repo how2heap reste la référence pratique pour les PoC associés.

fastbin dup

Le principe ici c'est juste de comprendre le double-free. C'est très connu, ça se comprend facilement : on free() deux fois un même chunk, et lors des allocations suivantes ça va foutre la merde. Ce qu'on va essayer de trigger avec un double-free c'est clairement un arbitrary-write. En fait, puisqu'on va contrôler le pointeur qui se retrouve dans le fastbin, on peut orienter la prochaine allocation vers n'importe quelle adresse.

fastbin-dup.txt
  Après malloc(0x20) + free(A) + free(A)  [sans mitigation]
  ──────────────────────────────────────────────────────────

  fastbin[0x20]
  ┌─────────────────────────────────────────────┐
  │  HEAD  →  chunk A  →  chunk A  →  NULL      │
  │            (fd)         (fd)                │
  └─────────────────────────────────────────────┘
         ▲           ▲
         └───────────┘
          même adresse !

  malloc(0x20) → retourne chunk A
  malloc(0x20) → retourne chunk A  (encore !)
  malloc(0x20) → retourne ptr qu'on a écrit dans fd

  => arbitrary-write : on contrôle où pointe fd
Double-free : le même chunk apparaît deux fois dans la fastbin list.
Mitigation tcache
Depuis glibc 2.26, le tcache (Thread Cache) intercepte les allocations avant les fastbins. Un double-free naïf est détecté dès la 2e tentative via le bit key dans le chunk. Il faut contourner ça avant de jouer avec les fastbins.

unlink()

La technique unlink exploite la macro unlink() de ptmalloc2 qui retire un chunk de sa bin doublement chainée. En falsifiant les pointeurs fd / bk d'un chunk adjacent, on peut déclencher une écriture arbitraire au moment où ptmalloc2 met à jour ses listes.

En cours de rédaction
Section à développer. Voir unsafe_unlink.c dans how2heap pour le PoC de référence.

house of spirit

On forge un faux chunk sur la stack (ou dans une zone qu'on contrôle) et on le free(). ptmalloc2 l'accepte et l'insère dans la fastbin correspondante. Le prochain malloc() de la même taille retourne notre zone contrôlée.

En cours de rédaction
Section à développer. Voir house_of_spirit.c dans how2heap pour le PoC.

null byte overflow

Un off-by-one qui écrase le byte de poids faible du champ size d'un chunk adjacent par 0x00. On peut ainsi effacer le bit PREV_INUSE et/ou réduire la taille apparente du chunk pour créer des overlaps ou déclencher une consolidation incorrecte.

En cours de rédaction
Section à développer.

house of lore

Exploitation de la smallbin : on falsifie les pointeurs bk d'un chunk en smallbin pour faire pointer le retour de malloc() vers une adresse arbitraire lors de l'allocation suivante.

En cours de rédaction
Section à développer. Voir house_of_lore.c dans how2heap.

chunk overlapping

En manipulant les métadonnées de taille des chunks, on amène ptmalloc2 à retourner deux allocations qui se chevauchent en mémoire. On peut alors modifier les données d'une allocation via une autre, ce qui permet corruption de pointeurs ou de métadonnées heap.

En cours de rédaction
Section à développer.

house of force

On écrase le champ size du top chunk (wilderness) avec une valeur immense (ex. 0xffffffffffffffff). ptmalloc2 peut alors allouer un chunk à n'importe quelle adresse virtuelle, y compris avant le top chunk actuel, ce qui donne un arbitrary-write.

Mitigation glibc 2.29+
Depuis glibc 2.29, une vérification sur la taille du top chunk bloque cette technique dans sa forme naïve.

unsorted bin attack

On corrompt le pointeur bk d'un chunk dans l'unsorted bin. Lors de la prochaine allocation qui passe par l'unsorted bin, ptmalloc2 écrit l'adresse de l'unsorted bin (&main_arena.bins[0]) à l'adresse qu'on a mise dans bk. Utile pour écraser un compteur ou contourner une protection.

En cours de rédaction
Section à développer.

large bin attack

Variante de l'unsorted bin attack visant les large bins. En falsifiant les pointeurs bk et bk_nextsize, on peut écrire deux valeurs arbitraires lors de l'insertion d'un chunk dans un large bin.

En cours de rédaction
Section à développer.

house of einherjar

On combine un null byte overflow avec une falsification du champ prev_size pour faire croire à ptmalloc2 qu'un chunk précédent est libre alors qu'il ne l'est pas. La consolidation backward provoque un overlap de chunks.

En cours de rédaction
Section à développer. Voir house_of_einherjar.c dans how2heap.

house of orange

Technique en deux étapes : d'abord un overflow sur le top chunk pour forcer ptmalloc2 à allouer via sysmalloc() et insérer l'ancien top chunk dans l'unsorted bin. Ensuite, on corrompt ce chunk unsorted pour pivoter vers un _IO_FILE et hijacker le flow via les vtables de la libc (FILE stream exploitation).

Mitigation glibc 2.24+
La validation des vtables _IO_FILE a été renforcée en glibc 2.24. house of orange dans sa forme originale ne fonctionne plus sans contournement supplémentaire.

house of roman

Technique sans leak : on exploite une corruption partielle de pointeur (1,5 bytes) dans le tcache / fastbin pour faire pointer une allocation vers __malloc_hook en devinant les bits de l'adresse libc par brute-force (1/4096 de chance avec ASLR, faisable en remote à faible coût).

En cours de rédaction
Section à développer. Voir house_of_roman.c dans how2heap.

Techniques tcache

Le tcache (Thread Cache Allocation, introduit en glibc 2.26) ajoute une couche de bins per-thread très peu vérifiée (à l'origine) qui a ouvert toute une famille de nouvelles attaques.

tcache dup

Équivalent du fastbin dup mais dans le tcache. Avant glibc 2.29, aucune vérification de double-free. Depuis 2.29, le champ key dans le chunk freé pointe vers le tcache courant - un double-free naïf est détecté.

En cours de rédaction
Section à développer. Voir tcache_dup.c.

tcache poisoning

On corrompt le pointeur next d'un chunk dans la tcache list pour faire pointer la prochaine allocation vers une adresse arbitraire. Avant glibc 2.32, ce pointeur n'est pas chiffré : écriture directe, aucun gadget nécessaire. Depuis 2.32, le safe linking XOR le pointeur avec L >> 12 (voir section dédiée).

tcache-poisoning.txt
  tcache bin[0x20]
  ┌────────────────────────────────────────────────┐
  │  count = 2                                     │
  │  HEAD → chunk A → chunk B → NULL               │
  └────────────────────────────────────────────────┘

  On corrompt chunk A :
    chunk A->next  = &target   (ex: __free_hook, stack var...)

  tcache bin[0x20] après corruption :
  ┌────────────────────────────────────────────────┐
  │  count = 2                                     │
  │  HEAD → chunk A → &target → ???                │
  └────────────────────────────────────────────────┘

  malloc(0x20) → retourne chunk A   (légitime)
  malloc(0x20) → retourne &target   (arbitrary alloc !)
tcache poisoning : on redirige next vers target pour que malloc() retourne target.

tcache house of spirit

Même principe que la house of spirit classique, mais on vise le tcache au lieu des fastbins. Plus simple : le tcache vérifie beaucoup moins les métadonnées du chunk freé (pas de vérification d'alignement du next chunk avant glibc 2.29).

En cours de rédaction
Section à développer.

house of botcake

Contourne la détection de double-free du tcache (champ key glibc 2.29+) en alternant des frees entre tcache et unsorted bin pour vider puis réinsérer un chunk dans le tcache sans que le key soit encore actif. Résultat : deux pointeurs vers le même chunk, arbitrary-write.

En cours de rédaction
Section à développer. Voir house_of_botcake.c.

tcache stashing unlink()

Exploite le mécanisme de "stashing" : quand ptmalloc2 puise dans une smallbin et que le tcache a de la place, les chunks restants sont stashés dans le tcache. En falsifiant le pointeur bk d'un chunk smallbin, on peut insérer une adresse arbitraire dans le tcache, puismalloc() la fait retourner.

En cours de rédaction
Section à développer. Voir tcache_stashing_unlink_attack.c.

fastbin reverse into tcache

Quand une fastbin est vidée, les chunks restants sont transférés dans le tcache. En corrompant les pointeurs fd des chunks fastbin avant ce transfert, on injecte des adresses arbitraires dans le tcache. Utile pour contourner des vérifications fastbin tout en profitant de la souplesse du tcache.

En cours de rédaction
Section à développer. Voir fastbin_reverse_into_tcache.c.

house of mind fastbin

On forge une fausse arena dans une zone contrôlée (souvent dans la heap elle-même) et on manipule le bit NON_MAIN_ARENA d'un chunk pour qu'il soit rattaché à cette fausse arena. Les opérations malloc/free suivantes interagiront avec nos structures falsifiées.

En cours de rédaction
Section à développer.

house of storm

Combine unsorted bin attack et large bin attack pour obtenir deux écritures arbitraires simultanées, permettant de contourner des protections qui rendraient chacune des deux attaques seule insuffisante.

En cours de rédaction
Section à développer. Voir house_of_storm.c.

Déchiffrer le safe linking

Depuis glibc 2.32, ptmalloc2 chiffre les pointeurs next / fd des chunks free dans le tcache et les fastbins via le mécanisme de safe linking (SL) :

safe-linking.txt
  Stocké en mémoire (chunk->next) :
    ptr_chiffré = ptr_réel  XOR  (adresse_du_slot >> 12)

  Pour lire le pointeur réel :
    ptr_réel = ptr_chiffré  XOR  (adresse_du_slot >> 12)

  Pourquoi >> 12 ? Les 12 bits bas d'une adresse heap sont
  souvent prévisibles (alignement page). XOR avec les bits
  hauts introduit de l'entropie sans coût.

  Pour empoisonner le tcache malgré SL :
    1. Leak une adresse heap (ex: via unsorted bin, %p fmt, ...)
    2. Calculer L = adresse_du_slot >> 12
    3. Écrire  ptr_cible XOR L  dans chunk->next
Safe linking : le pointeur next est XOR'd avec l'adresse du slot décalée de 12 bits.
Contourner le safe linking
Si on a un leak d'adresse heap, on peut recalculer le masque et empoisonner le tcache normalement. Sans leak, on peut parfois exploiter des bits partiels connus (les 12 bits bas de l'adresse du slot sont toujours 0 par alignement page, donc L & 0xfff == 0).