Aller au contenu

CWE-122 : débordement de tampon dans le tas

Seizième du CWE Top 25 2025, et dernière des six entrées de la famille mémoire. Le libellé officiel est Heap-based Buffer Overflow, « débordement de tampon fondé sur le tas ». La définition du MITRE précise ce qui distingue cette variante : la zone débordée est allouée dans la partie de la mémoire appelée tas, ce qui signifie généralement qu’elle a été demandée au système pendant l’exécution.

→ Lire l’explication complète de la marmotte

Comme sa sœur de la pile, cette entrée ne décrit pas une cause nouvelle : la cause reste une copie dont la taille n’a pas été vérifiée, et le geste reste une écriture hors limites. Elle localise la zone touchée, et cette localisation change tout de la suite.

Le tas n’est pas organisé comme la pile. Il ne contient pas d’adresses de retour, mais il contient deux choses tout aussi intéressantes :

  • les objets voisins de celui qu’on déborde, c’est-à-dire tout ce que l’application a alloué dynamiquement : chaînes, structures, et surtout objets dotés de pointeurs vers des fonctions ;
  • les métadonnées de l’allocateur, ces informations de service qui indiquent la taille des blocs et leur enchaînement, et dont la corruption permet d’influencer les allocations suivantes.

Le débordement de tas n’offre donc pas un détournement immédiat du fil d’exécution : il offre un moyen de modifier l’état d’un programme, que l’attaquant transforme ensuite en exécution par un chemin propre à l’application visée.

CWE-122 est une variante, fille directe de CWE-787. Elle n’a pas d’entrée fille, et c’est normal : on ne descend pas sous la localisation.

Son voisinage le plus significatif n’est pas dans la généalogie mais dans l’usage : l’usage après libération habite le même tas, et les deux faiblesses se combinent couramment dans une même chaîne d’attaque. L’une écrit au-delà d’un bloc, l’autre réutilise un bloc rendu ; les deux visent à placer des données choisies à un endroit que le programme croit sien. C’est pourquoi les travaux de durcissement des allocateurs (séparation des métadonnées, répartition aléatoire, vérifications d’intégrité) traitent les deux ensemble.

Parce que les protections de la pile ne la concernent pas. Le canari protège l’adresse de retour d’une fonction ; il n’y a pas d’adresse de retour dans le tas. La non-exécutabilité des données empêche d’exécuter ce qu’on y écrit, ce qui a déplacé l’exploitation vers la réutilisation de code déjà présent, sans rien supprimer de la faiblesse elle-même.

Parce que les gros logiciels modernes vivent dans le tas. Un navigateur, un moteur de base de données, un hyperviseur allouent dynamiquement des millions d’objets dont la taille dépend des données traitées. Les décodeurs de formats (images, vidéos, polices, archives) sont les premiers concernés, et ils traitent par définition des données venues de l’extérieur.

Parce que l’exploitation est devenue une discipline. La maîtrise de la disposition du tas est un savoir-faire documenté et outillé ; une fois acquis, il rend exploitables des débordements de quelques octets, ce qui élargit considérablement l’ensemble des vulnérabilités classées critiques. Le classement, pondéré par la gravité, en enregistre l’effet.

Les bases sont posées par comment un débordement écrase la pile, qui explique ce qu’est un tampon et ce qui l’entoure ; le tas se comprend ensuite comme la même chose sans adresse de retour et avec des métadonnées.

Pour les contre-mesures, ASLR, DEP, NX, canaris et CFI détaille ce qui freine l’exploitation, et c’est la page qui permet de comprendre la stratégie défensive actuelle : rendre le coût de l’exploitation élevé plutôt que nul, à défaut de pouvoir corriger tout le code existant.

Pour la suppression de la classe, Rust et la sécurité mémoire par construction décrit la vérification des bornes et le modèle de propriété, qui traitent du même coup le débordement et l’usage après libération : les deux faiblesses du tas.

Choisir un allocateur durci et le laisser faire son travail. Les allocateurs modernes isolent les métadonnées, répartissent les blocs de façon imprévisible et détectent certaines corruptions. Ce n’est pas une correction, c’est un coût imposé à l’attaquant, et il est généralement obtenu par un changement de configuration plutôt que de code.

Tester les décodeurs par génération d’entrées, avec un analyseur mémoire activé. C’est la mesure qui trouve ces défauts avant leur publication, et elle s’applique précisément aux composants les plus exposés.

Réécrire les parseurs dans un langage à sûreté mémoire, en commençant par ceux qui lisent des formats complexes venus du réseau. C’est l’investissement que les grands éditeurs ont consenti pour leurs navigateurs et leurs noyaux, et il s’explique par un calcul simple : ce sont ces composants qui produisent la majorité des vulnérabilités critiques.

Une équipe qui croit la question mémoire réglée par les protections du compilateur applique à son code de tas des défenses conçues pour la pile, et obtient le pire des deux mondes : la confiance sans la protection. Les symptômes sont trompeurs : un plantage aléatoire, à un endroit sans rapport avec le défaut, souvent attribué à un matériel défaillant ou à une version de bibliothèque. Le défaut survit donc à plusieurs cycles de maintenance, parfois des années, parce que personne ne l’a jamais reproduit en laboratoire. Quand il est finalement exploité, la conséquence n’est pas une panne mais l’exécution de code dans le processus visé, qui est souvent le composant le plus privilégié de la chaîne : un décodeur d’images dans un navigateur, un pilote dans un noyau, un module de supervision dans un hyperviseur. Le coût de l’enquête est aggravé par l’absence de trace : rien, dans les journaux applicatifs, ne distingue une allocation malveillante d’une allocation ordinaire.

Un programme a deux façons de réserver de la place en mémoire. La première, la pile, sert aux variables de travail des fonctions en cours : elle s’empile et se dépile automatiquement. La seconde, le tas, sert aux données dont on ne connaît la taille qu’au dernier moment : le programme demande au système un espace, s’en sert, puis le rend.

Dans le tas, les espaces attribués sont voisins les uns des autres, et le système tient un registre de ce qui a été attribué à qui. Si un programme écrit plus de données que son espace n’en contient, le surplus déborde sur l’espace du voisin (un autre objet de la même application), et parfois sur le registre lui-même.

C’est le défaut que le catalogue CWE appelle CWE-122. Il n’a pas de conséquence immédiate aussi spectaculaire que son cousin de la pile, qui permet de détourner directement le cours du programme. Il est en revanche plus difficile à contrer, pour une raison précise : les protections ajoutées depuis vingt ans ont été conçues pour la pile, où l’on sait exactement quelle information précieuse protéger. Dans le tas, il n’y a pas une information précieuse mais des milliers d’objets, et ce qui se trouve à côté de l’espace débordé dépend de ce que l’application a fait juste avant.

Les attaquants ont donc appris à influencer cet ordre : en envoyant certaines requêtes dans un certain ordre, on obtient que l’objet intéressant se trouve juste après celui que l’on sait déborder. C’est un savoir-faire technique considérable, qui rend exploitables des défauts portant sur quelques caractères. C’est aussi pourquoi les éditeurs de navigateurs et de systèmes d’exploitation réécrivent progressivement, dans des langages plus stricts, les composants qui lisent des images, des vidéos et des polices de caractères.

À lire aussi

Pages voisines

: les deux pages se citent l’une l’autre.