Un bug de débordement n’est pas, en soi, une exploitation. Entre le bug et le contrôle du flot d’exécution s’interposent des protections, cumulatives sur un binaire moderne, qui déterminent ce qu’un attaquant peut réellement en faire. Détecter lesquelles sont actives — via checksec, readelf, objdump — est le premier réflexe face à un binaire inconnu, car cela oriente entièrement la stratégie.

ProtectionPrincipeCe qu’elle empêcheSes limites
Stack CanaryLe compilateur insère une valeur aléatoire avant l’adresse de retour ; vérifiée avant le retour, elle déclenche un abort si altérée.L’écrasement de l’adresse de retour par débordement de pile.N’empêche ni l’écrasement d’autres données, ni le heap overflow, ni les techniques non classiques (format-string, write-what-where, use-after-free).
NX / DEPLes pages de données (pile, tas) ne sont pas marquées exécutables.L’injection puis l’exécution d’un shellcode classique.Le ROP la contourne entièrement (voir rop) : on réutilise du code déjà exécutable, sans jamais injecter d’instruction nouvelle.
ASLR / PIELe loader randomise à chaque exécution l’emplacement des segments — pile, tas, bibliothèques, et base de l’exécutable si compilé en PIE.La prédiction d’adresses, donc l’écrasement « à l’aveugle » d’un retour ou d’une chaîne ROP.Une seule fuite d’adresse (infoleak) suffit à la contourner localement (voir pie-aslr).
RELROEn full RELRO, la GOT est résolue intégralement au lancement puis passée en lecture seule.Le GOT overwrite, qui redirige un appel de fonction externe.Ne protège ni le ROP, ni le heap overflow, ni la corruption d’autres données (voir got-plt-relro).

Pourquoi elles se combinent

Ces protections sont cumulatives : un binaire moderne réunit typiquement canari, NX, ASLR, RELRO et PIE, ce qui rend l’exploitation nettement plus ardue — mais jamais impossible. Chaque protection ferme un vecteur précis, et un attaquant compétent adapte sa stratégie en conséquence : ROP pour contourner NX, fuite d’information pour contourner ASLR, ret2libc ou gadgets non-GOT pour contourner RELRO. Un bug n’est exploitable que dans le bon contexte : un buffer overflow non protégé par canari, sans NX ni ASLR, se résout en une simple redirection ; le même bug, sous protections complètes, exige une chaîne d’actions — fuite, puis calcul d’adresse, puis chaîne de gadgets.

Une protection à part : FORTIFY_SOURCE

_FORTIFY_SOURCE agit à un autre niveau : celui de la compilation et de la bibliothèque C, non de la mémoire du processus. Activé (-D_FORTIFY_SOURCE=2 ou, depuis glibc 2.34, =3), il substitue aux fonctions dangereuses de manipulation de chaînes et de mémoire (memcpy, strcpy, sprintf) des variantes « fortifiées » (__xxx_chk) qui vérifient, avant d’écrire, que la destination dispose d’assez d’espace — et interrompent le programme (*** buffer overflow detected ***) si ce n’est pas le cas1.

Sa portée reste toutefois étroite. Elle ne couvre que les fonctions de la bibliothèque C qu’elle remplace : tout code manuel — arithmétique de pointeurs, read(), recv(), allocateurs maison — lui échappe entièrement. Elle exige par ailleurs un niveau d’optimisation suffisant (-O1 ou plus) pour que les vérifications soient effectivement insérées, et ne protège en rien contre la corruption de tas, l’use-after-free ou la corruption de structures complexes. C’est un filet de sécurité contre les débordements les plus naïfs, pas un rempart contre l’exploitation binaire dans son ensemble — et c’est précisément ce qui en fait un faux ami potentiel : un binaire qui semble « durci » peut rester parfaitement exploitable dès qu’on s’écarte des fonctions qu’elle couvre.

Footnotes

  1. Red Hat, Enhance application security with FORTIFY_SOURCE.