Analyse de _FORTIFY_SOURCE dans le contexte des protections / durcissements de binaires.
Qu’est-ce que _FORTIFY_SOURCE
-
_FORTIFY_SOURCEest une macro / mécanisme de durcissement qui agit au niveau de la compilation + runtime pour fournir une « protection légère » contre certains débordements mémoire (buffer-overflow) et vulnérabilités liées aux chaînes / fonctions de manipulation mémoire1. -
Concrètement, quand
_FORTIFY_SOURCEest activé (par exemple via-D_FORTIFY_SOURCE=2ou, plus récemment,-D_FORTIFY_SOURCE=3), le compilateur (GCC / clang) remplace certaines fonctions « dangereuses » (commememcpy,strcpy,sprintf) par des versions « fortifiées » — des wrappers (souvent appelés__xxx_chk) qui, avant d’effectuer la copie ou l’écriture, vérifient que la taille de la destination est suffisante, et abortent (crash) le programme si ce n’est pas le cas2. -
Le niveau de protection dépend de la version :
_FORTIFY_SOURCE=1: quelques vérifications, sans changer le comportement des programmes conformes1 ;_FORTIFY_SOURCE=2: plus de fonctions « fortifiées »1 ;- depuis glibc 2.34 / GCC 12, un niveau
_FORTIFY_SOURCE=3existe pour améliorer la couverture — il essaie d’évaluer plus précisément la taille des objets passés aux fonctions, ce qui rend les vérifications plus robustes3.
-
L’impact en runtime / performance est jugé faible : l’ajout de ces vérifications n’engendre pas — en pratique — un surcoût systématique significatif4.
Ce que ça protège (et ce que ça empêche)
Quand _FORTIFY_SOURCE est actif :
- Les appels à des fonctions string/memory potentiellement dangereux sont « interceptés ». Avant d’effectuer l’opération, la version
_chkvérifie que la destination a assez de place — ce qui empêche un overflow visible dans des cas simples (par exemplestrcpy(dest, src)oùsrcdépasse la taille dedest)1. - Si l’appel viole la condition (overflow possible), le programme est immédiatement interrompu — généralement avec un message type
*** buffer overflow detected ***et un core-dump5. - Cela couvre un certain nombre de fonctions « à risque » :
memcpy,memmove,strcpy,strcat,sprintf1.
Donc _FORTIFY_SOURCE fournit une mitigation « préventive » contre des débordements basiques liés aux buffers — un peu comme un « filet de sécurité » (fail-safe) pour des fonctions classiques de manipulation mémoire / chaînes.
Les limites de _FORTIFY_SOURCE — ce qu’il ne peut pas empêcher
Important : _FORTIFY_SOURCE n’est pas une protection universelle. Voici ses limites / cas où il ne sert à rien :
- Portée limitée des fonctions : seul un certain nombre de fonctions standard (
memcpy,strcpy) sont « fortifiées ». Tout autre code (manipulation manuelle, arithmétique de pointeurs, usage deread(),recv(),gets()non remplacée) n’est pas protégé par_FORTIFY_SOURCE6. - Obligation d’optimisation / compilation « correcte » : les vérifications fonctionnent quand le code est compilé avec un certain niveau d’optimisation (souvent
-O1ou plus) et les bons flags (_FORTIFY_SOURCE), sinon les checks peuvent ne pas être insérés1. - Complexité des cas dynamiques : si les tampons sont alloués dynamiquement, via des allocateurs personnalisés, ou manipulés d’une façon non triviale, le compilateur peut ne pas être capable de déterminer la « bonne » taille à vérifier → la protection échoue4.
- Ne protège ni contre les corruptions de heap/stack complexes : un overflow de pile, un débordement sur heap, corruptions via use-after-free, corruption de structures, ne sont pas couverts — sauf si le bug passe par une des fonctions « fortifiées »7.
_FORTIFY_SOURCEc’est un filet, mais loin d’être exhaustif. - Ne remplace pas d’autres protections : c’est une brique parmi d’autres. D’autres mitigations comme RELRO, NX, ASLR, canaris de pile restent nécessaires pour un hardening complet.
En tant qu’ingénieur sécurité / reverse-engineer : ce que ça signifie
Vu la préparation à des exercices de reverse / exploitation, _FORTIFY_SOURCE a des implications intéressantes (et des pièges) :
- Si l’on tombe sur un binaire compilé avec
_FORTIFY_SOURCE, des « overflows simples » viastrcpy,memcpypeuvent être plus difficiles — le programme crash plutôt que de permettre l’écrasement silencieux. Cela pourrait rendre certaines primitives d’exploitation (stack overflow + ret2…) plus difficiles. - Mais ce n’est qu’une protection « fragile » : si le binaire utilise des fonctions custom, des lectures directes (
read), des manipulations de mémoire mal encadrées, ou des allocateurs maison →_FORTIFY_SOURCEne nous empêchera probablement pas d’exploiter la vulnérabilité. - Donc : ne jamais miser sur
_FORTIFY_SOURCEcomme seul bouclier — c’est un plus, mais il faut toujours vérifier l’absence d’autres vecteurs d’attaque (heap, stack, use-after-free, corruption de structures).
_FORTIFY_SOURCE vs RELRO / autres hardenings — ce qu’ils apportent de différent
| Protection | Objectif principal | Ce qu’elle protège | Ce qu’elle ne protège pas |
|---|---|---|---|
_FORTIFY_SOURCE | Vérifications / sécurisation des fonctions memory/string | Buffer-overflows simples liés à strcpy/memcpy | Overflows manuels, heap/stack/alloc dynamiques, use-after-free, corruption mémoire complexe |
| RELRO (full) | Rendre la GOT/PLT en lecture-seule après relocation | Empêcher les GOT-overwrite (redirection de fonctions) | Overflows de pile, heap, format-string, corruption autres sections |
| NX, ASLR, canari, PIE | Diverses : empêcher exécution de shellcode, randomiser adresses, détecter corruption de pile | Large panel (injection, ROP, ret2libc, corruption retour) | Ne protègent pas les bugs logiques, les fuites d’information |
Ces protections sont complémentaires — _FORTIFY_SOURCE est une protection au niveau du source + runtime, tandis que RELRO, NX, etc. agissent au niveau binaire / mémoire / runtime.
Conclusion
_FORTIFY_SOURCEest un « gadget » intéressant pour durcir des programmes C/C++, en particulier contre des débordements mémoire simples.- Ce n’est pas une panacée : beaucoup de cas d’exploitation restent possibles, surtout dans un contexte de reverse / pwn / exploitation — il ne remplace pas les protections « à bas niveau » comme RELRO, NX, ASLR.
- Dans un contexte d’étude / exploitation, c’est surtout un faux ami potentiel : il peut donner l’impression qu’un binaire est « durci / safe », mais dès qu’on descend dans le détail (heap, allocations dynamiques, logique, overflows manuels), il faut rester vigilant.