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
- Compréhension des fichiers ELF
- ptmalloc2 - Introduction
- ptmalloc2 - Chunks
- ptmalloc2 - Arenas
- ptmalloc2 - Bins
- ptmalloc2 - TCache (Thread Cache)
- Maniement de GDB
- Introduction PWN
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
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.
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 fdMitigation tcache
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
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
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
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
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
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+
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
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
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
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+
_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
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
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 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 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
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
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
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
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
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
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) :
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->nextContourner le safe linking
L & 0xfff == 0).Article suivant
ROP, JOP, SROP : recycler le code pour gagner