Relro

Reading time: 7 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Relro

RELRO signifie Relocation Read-Only et c'est une attĂ©nuation mise en Ɠuvre par le linker (ld) qui rend un sous-ensemble des segments de donnĂ©es ELF lecture seule aprĂšs que toutes les relocations ont Ă©tĂ© appliquĂ©es. L'objectif est d'empĂȘcher un attaquant de remplacer des entrĂ©es dans la GOT (Global Offset Table) ou d'autres tables liĂ©es aux relocations qui sont dĂ©rĂ©fĂ©rencĂ©es pendant l'exĂ©cution du programme (par exemple, __fini_array).

Les linkers modernes mettent en Ɠuvre RELRO en rĂ©organisant la GOT (et quelques autres sections) afin qu'elles se trouvent avant le .bss et – surtout – en crĂ©ant un segment PT_GNU_RELRO dĂ©diĂ© qui est remappĂ© R–X juste aprĂšs que le chargeur dynamique ait terminĂ© d'appliquer les relocations. Par consĂ©quent, les dĂ©bordements de tampon typiques dans le .bss ne peuvent plus atteindre la GOT et les primitives d'Ă©criture arbitraire ne peuvent pas ĂȘtre utilisĂ©es pour Ă©craser des pointeurs de fonction qui se trouvent Ă  l'intĂ©rieur d'une page protĂ©gĂ©e par RELRO.

Il existe deux niveaux de protection que le linker peut émettre :

Partial RELRO

  • Produit avec le drapeau -Wl,-z,relro (ou juste -z relro lors de l'invocation directe de ld).
  • Seule la partie non-PLT de la GOT (la partie utilisĂ©e pour les relocations de donnĂ©es) est placĂ©e dans le segment en lecture seule. Les sections qui doivent ĂȘtre modifiĂ©es Ă  l'exĂ©cution – surtout .got.plt qui prend en charge lazy binding – restent modifiables.
  • À cause de cela, une primitive d'Ă©criture arbitraire peut toujours rediriger le flux d'exĂ©cution en Ă©crasant une entrĂ©e PLT (ou en effectuant ret2dlresolve).
  • L'impact sur les performances est nĂ©gligeable et donc presque toutes les distributions expĂ©dient des paquets avec au moins Partial RELRO depuis des annĂ©es (c'est le dĂ©faut de GCC/Binutils depuis 2016).

Full RELRO

  • Produit avec les deux drapeaux -Wl,-z,relro,-z,now (a.k.a. -z relro -z now). -z now force le chargeur dynamique Ă  rĂ©soudre tous les symboles Ă  l'avance (liaison impatiente) afin que .got.plt n'ait jamais besoin d'ĂȘtre Ă©crit Ă  nouveau et puisse ĂȘtre mappĂ© en toute sĂ©curitĂ© en lecture seule.
  • L'ensemble de la GOT, .got.plt, .fini_array, .init_array, .preinit_array et quelques tables internes supplĂ©mentaires de glibc se retrouvent Ă  l'intĂ©rieur d'un segment PT_GNU_RELRO en lecture seule.
  • Ajoute un surcoĂ»t de dĂ©marrage mesurable (toutes les relocations dynamiques sont traitĂ©es au lancement) mais aucun surcoĂ»t Ă  l'exĂ©cution.

Depuis 2023, plusieurs distributions grand public ont commencĂ© Ă  compiler la tool-chain systĂšme (et la plupart des paquets) avec Full RELRO par dĂ©faut – par exemple Debian 12 “bookworm” (dpkg-buildflags 13.0.0) et Fedora 35+. En tant que pentester, vous devez donc vous attendre Ă  rencontrer des binaires oĂč chaque entrĂ©e GOT est en lecture seule.


Comment vérifier le statut RELRO d'un binaire

bash
$ checksec --file ./vuln
[*] '/tmp/vuln'
Arch:     amd64-64-little
RELRO:    Full
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

checksec (fait partie de pwntools et de nombreuses distributions) analyse les en-tĂȘtes ELF et affiche le niveau de protection. Si vous ne pouvez pas utiliser checksec, comptez sur readelf:

bash
# Partial RELRO → PT_GNU_RELRO is present but BIND_NOW is *absent*
$ readelf -l ./vuln | grep -E "GNU_RELRO|BIND_NOW"
GNU_RELRO      0x0000000000600e20 0x0000000000600e20
bash
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
$ readelf -d ./vuln | grep BIND_NOW
0x0000000000000010 (FLAGS)              FLAGS: BIND_NOW

Si le binaire est en cours d'exécution (par exemple, un helper set-uid root), vous pouvez toujours inspecter l'exécutable via /proc/$PID/exe :

bash
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO

Activer RELRO lors de la compilation de votre propre code

bash
# GCC example – create a PIE with Full RELRO and other common hardenings
$ gcc -fPIE -pie -z relro -z now -Wl,--as-needed -D_FORTIFY_SOURCE=2 main.c -o secure

-z relro -z now fonctionne pour GCC/clang (passé aprÚs -Wl,) et ld directement. Lorsque vous utilisez CMake 3.18+, vous pouvez demander Full RELRO avec le préréglage intégré :

cmake
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) # LTO
set(CMAKE_ENABLE_EXPORTS OFF)
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-z,relro,-z,now")

Techniques de contournement

Niveau RELROPrimitive typiqueTechniques d'exploitation possibles
Aucun / PartielÉcriture arbitraire1. Écraser l'entrĂ©e .got.plt et pivoter l'exĂ©cution.
2. ret2dlresolve – crĂ©er un faux Elf64_Rela & Elf64_Sym dans un segment Ă©crivable et appeler _dl_runtime_resolve.
3. Écraser les pointeurs de fonction dans .fini_array / liste atexit().
CompletGOT en lecture seule1. Rechercher d'autres pointeurs de code écrivable (vtables C++, __malloc_hook < glibc 2.34, __free_hook, rappels dans des sections .data personnalisées, pages JIT).
2. Abuser des primitives de lecture relative pour divulguer libc et effectuer SROP/ROP dans libc.
3. Injecter un objet partagé malveillant via DT_RPATH/LD_PRELOAD (si l'environnement est contrÎlé par l'attaquant) ou ld_audit.
4. Exploiter format-string ou écriture partielle de pointeur pour détourner le flux de contrÎle sans toucher le GOT.

💡 MĂȘme avec un RELRO Complet, le GOT des bibliothĂšques partagĂ©es chargĂ©es (par exemple, libc elle-mĂȘme) est seulement Partiel RELRO car ces objets sont dĂ©jĂ  mappĂ©s lorsque le chargeur applique les relocations. Si vous obtenez une primitive d'Ă©criture arbitraire qui peut cibler les pages d'un autre objet partagĂ©, vous pouvez toujours pivoter l'exĂ©cution en Ă©crasant les entrĂ©es GOT de libc ou la pile __rtld_global, une technique rĂ©guliĂšrement exploitĂ©e dans les dĂ©fis CTF modernes.

Exemple de contournement dans le monde rĂ©el (CTF 2024 – pwn.college “enlightened”)

Le défi était livré avec un RELRO Complet. L'exploitation a utilisé un off-by-one pour corrompre la taille d'un morceau de tas, a divulgué libc avec tcache poisoning, et enfin a écrasé __free_hook (en dehors du segment RELRO) avec un one-gadget pour obtenir l'exécution de code. Aucune écriture dans le GOT n'était requise.


Recherches récentes & vulnérabilités (2022-2025)

  • glibc 2.40 dĂ©prĂ©cie __malloc_hook / __free_hook (2025) – La plupart des exploits de tas modernes qui abusaient de ces symboles doivent maintenant pivoter vers des vecteurs alternatifs tels que rtld_global._dl_load_jump ou les tables d'exceptions C++. Étant donnĂ© que les hooks vivent en dehors du RELRO, leur suppression augmente la difficultĂ© des contournements de RELRO Complet.
  • Correction “max-page-size” de Binutils 2.41 (2024) – Un bug permettait aux derniers octets du segment RELRO de partager une page avec des donnĂ©es Ă©crivables sur certaines constructions ARM64, laissant un petit Ă©cart RELRO qui pouvait ĂȘtre Ă©crit aprĂšs mprotect. En amont, PT_GNU_RELRO est maintenant alignĂ© sur les limites de page, Ă©liminant ce cas particulier.

Références

  • Documentation de Binutils – -z relro, -z now et PT_GNU_RELRO
  • “RELRO – Complet, Partiel et Techniques de Contournement” – article de blog @ wolfslittlered 2023

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks