Qu'est-ce que Débordement de tampon (buffer overflow) ?

Fait d'écrire dans une zone comme un tableau (un tampon) plus de données qu'elle ne peut en contenir, en corrompant la mémoire voisine. En C, il se produit facilement avec des fonctions qui ne connaissent pas la taille de la destination, comme strcpy et gets, ou avec des boucles qui utilisent des indices sans vérifier les bornes, et c'est un comportement indéfini. Lorsqu'un tampon sur la pile déborde, des variables locales et l'adresse de retour de la fonction peuvent être écrasées, ce qui peut être exploité par des attaques qui font exécuter du code arbitraire au moyen d'entrées forgées. On l'évite avec des fonctions qui prennent une taille et avec des vérifications de bornes.

Nom japonais: バッファオーバーフロー

« Débordement de tampon (buffer overflow) » apparaît dans 3 questions d'entraînement réparties sur 2 thèmes, dont Éditeurs de liens, chargeurs et exécutables, Bases de la programmation en C.

Domaine: Bases de la programmation en C

Causes fréquentes

Les erreurs typiques sont l'oubli de l'octet du '\0' de fin lors d'un strcpy de 8 caractères dans char name[8], la lecture d'une entrée avec gets ou scanf("%s") sans vérifier sa longueur, et l'écriture d'un élément de trop, comme dans for (i = 0; i <= N; i++). Avec une fonction qui ne reçoit pas la taille de la destination, ce sont les données d'entrée qui décident de la quantité écrite.

Exploitation et défenses

L'adresse de retour de la fonction est sauvegardée près d'un tampon situé sur la pile. Un attaquant l'écrase au moyen d'une longue entrée et détourne l'exécution vers du code préparé ou vers des fonctions de bibliothèque. Les systèmes d'exploitation et les compilateurs rendent l'exploitation plus difficile avec la protection de la pile (canaris), qui vérifie une valeur placée avant l'adresse de retour, la séparation de la mémoire inscriptible et exécutable (W^X) et la randomisation des adresses (ASLR).

Prévention dans les programmes

Au lieu de strcpy, on utilise des fonctions qui prennent la taille à écrire, comme snprintf, et on vérifie toujours la longueur des entrées et la plage des indices. Pendant le développement, -fsanitize=address (AddressSanitizer) détecte à l'exécution les écritures hors limites. Sur un microcontrôleur sans MMU, un débordement n'arrête pas le programme tout de suite mais corrompt silencieusement les données voisines : il faut donc être particulièrement vigilant.

S'entraîner : questions sur Débordement de tampon (buffer overflow)

Quel est l'objectif de l'ASLR (randomisation de l'agencement de l'espace d'adressage) ?

  1. AChanger à chaque exécution les adresses de la pile, du tas, des bibliothèques, etc., pour qu'un attaquant puisse difficilement prévoir les adresses visées
  2. BChoisir les adresses mémoire au hasard pour améliorer le taux de succès du cache
  3. CLibérer au hasard la mémoire inutilisée pour augmenter l'espace libre
Afficher la réponse et l'explication

Réponse : A. Changer à chaque exécution les adresses de la pile, du tas, des bibliothèques, etc., pour qu'un attaquant puisse difficilement prévoir les adresses visées

Dans les attaques exploitant un débordement de tampon notamment, connaître l'adresse des fonctions de bibliothèque ou des données sur la pile est essentiel. L'ASLR décale à chaque démarrage de processus les adresses de la pile, du tas, des bibliothèques partagées et de l'exécutable, empêchant l'attaquant de viser des adresses fixes. Elle ne suffit pas seule à tout empêcher et s'utilise en combinaison avec d'autres mesures, comme la séparation écriture-exécution.

Quelle attaque est plus facile à prévenir en n'autorisant jamais simultanément « inscriptible » et « exécutable » sur une page mémoire (W^X, séparation écriture-exécution) ?

  1. AL'attaque qui essaie les mots de passe par force brute
  2. BL'attaque qui envoie un trafic massif pour arrêter un serveur
  3. CL'attaque qui écrit du code machine malveillant dans une zone de données comme la pile et y fait sauter l'exécution
Afficher la réponse et l'explication

Réponse : C. L'attaque qui écrit du code machine malveillant dans une zone de données comme la pile et y fait sauter l'exécution

Les anciennes attaques écrivaient souvent du langage machine sur la pile par débordement de tampon, puis redirigeaient l'adresse de retour vers celui-ci pour l'exécuter. Les tables de pages du CPU comportent un bit d'interdiction d'exécution (bit NX, etc.) ; si l'OS interdit l'exécution sur la pile, le tas et .data, et l'écriture sur .text, cette méthode devient inutilisable. Lorsque du code est généré à l'exécution, comme par un compilateur JIT, on bascule entre droits d'écriture et d'exécution.

Questions fréquentes

Un débordement de tampon arrête-t-il toujours le programme ?

Pas forcément. Si l'écriture excédentaire tombe dans une zone inutilisée, rien ne semble se passer, et si elle corrompt une autre variable, le programme donne des résultats faux ailleurs. Le programme peut ne s'arrêter anormalement que lorsque l'adresse de retour a été corrompue ou que la protection de la pile se déclenche.

strncpy est-il sûr ?

strncpy copie au plus le nombre de caractères indiqué, mais lorsque la chaîne source est plus longue, il n'ajoute pas le '\0' de fin : le traitement suivant risque alors de lire hors limites. On utilise snprintf, qui prend une taille et ajoute toujours le caractère de fin, ou on écrit soi-même ce caractère après strncpy.

« Débordement de tampon (buffer overflow) » dans les explications

Questions mentionnant « Débordement de tampon (buffer overflow) » (3)

  1. Quel est l'objectif de l'ASLR (randomisation de l'agencement de l'espace d'adressage) ?
  2. Quelle attaque est plus facile à prévenir en n'autorisant jamais simultanément « inscriptible » et « exécutable » sur une page mémoire (W^X, séparation écriture-exécution) ?
  3. La chaîne de 8 caractères "ABCDEFGH" est passée à la fonction save ci-dessus. Que se passe-t-il ?

Termes associés

Pile (stack) Tas (heap) Adresse de retour ASLR Variable Alignement et remplissage (padding) Allocation dynamique de mémoire Champ de bits (bit-field) Comportement indéfini (undefined behavior) Fuite de mémoire (memory leak)

Retour au glossaire