Relro
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Relro
RELRO significa Relocation Read-Only y es una mitigación implementada por el linker (ld) que convierte un subconjunto de los segmentos de datos ELF en solo lectura después de que se hayan aplicado todas las relocaciones. El objetivo es impedir que un atacante sobrescriba entradas en la GOT (Global Offset Table) u otras tablas relacionadas con relocaciones que se desreferencian durante la ejecución del programa (p. ej. __fini_array).
Los linkers modernos implementan RELRO re–ordenando la GOT (y algunas otras secciones) para que vivan antes de la .bss y —lo más importante— creando un segmento dedicado PT_GNU_RELRO que se remapea R–X justo después de que el cargador dinámico termina de aplicar las relocaciones. En consecuencia, los típicos buffer overflows en la .bss ya no pueden alcanzar la GOT y los primitivos de escritura arbitraria no pueden usarse para sobrescribir punteros a funciones que residen dentro de una página protegida por RELRO.
Hay dos niveles de protección que el linker puede emitir:
Partial RELRO
- Producido con la bandera
-Wl,-z,relro(o simplemente-z relrocuando se invocalddirectamente). - Solo la parte no-PLT de la GOT (la parte usada para relocaciones de datos) se coloca en el segmento de solo lectura. Las secciones que necesitan modificarse en tiempo de ejecución —lo más importante .got.plt que soporta lazy binding— permanecen escribibles.
- Debido a eso, un primitivo de arbitrary write aún puede redirigir el flujo de ejecución sobrescribiendo una entrada de la PLT (o realizando un ret2dlresolve).
- El impacto en rendimiento es insignificante y por lo tanto casi todas las distribuciones han estado entregando paquetes con al menos Partial RELRO durante años (es el default de GCC/Binutils desde 2016).
Full RELRO
- Producido con ambas banderas
-Wl,-z,relro,-z,now(a.k.a.-z relro -z now).-z nowfuerza al cargador dinámico a resolver todos los símbolos al inicio (eager binding) de modo que .got.plt nunca necesita volver a escribirse y puede mapearse de forma segura como solo lectura. - Toda la GOT, .got.plt, .fini_array, .init_array, .preinit_array y algunas tablas internas adicionales de glibc terminan dentro de un segmento
PT_GNU_RELROde solo lectura. - Añade una sobrecarga medible al inicio (todas las relocaciones dinámicas se procesan al lanzar) pero sin sobrecarga en tiempo de ejecución.
Desde 2023 varias distribuciones mainstream han cambiado a compilar la system tool-chain (y la mayoría de paquetes) con Full RELRO por defecto – p. ej. Debian 12 “bookworm” (dpkg-buildflags 13.0.0) y Fedora 35+. Como pentester deberías, por tanto, esperar encontrar binarios donde toda entrada de la GOT sea de solo lectura.
How to Check the RELRO status of a binary
$ checksec --file ./vuln
[*] '/tmp/vuln'
Arch: amd64-64-little
RELRO: Full
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
checksec (parte de pwntools y muchas distribuciones) analiza las cabeceras ELF e imprime el nivel de protección. Si no puedes usar checksec, usa readelf:
# 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
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
$ readelf -d ./vuln | grep BIND_NOW
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
Si el binario está en ejecución (p. ej. un set-uid root helper), aún puedes inspeccionar el ejecutable a través de /proc/$PID/exe:
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
Habilitar RELRO al compilar tu propio código
# 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 funciona tanto para GCC/clang (pasado después de -Wl,) como directamente para ld. Al usar CMake 3.18+ puedes solicitar Full RELRO con el preset incorporado:
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")
Técnicas de bypass
| RELRO level | Typical primitive | Possible exploitation techniques |
|---|---|---|
| Ninguno / Parcial | Escritura arbitraria | 1. Sobrescribir la entrada .got.plt y pivotar la ejecución. 2. ret2dlresolve – construir Elf64_Rela & Elf64_Sym falsos en un segmento escribible y llamar a _dl_runtime_resolve.3. Sobrescribir punteros a funciones en .fini_array / la lista de atexit(). |
| Completo | GOT es de solo lectura | 1. Buscar otros punteros de código escribibles (C++ vtables, __malloc_hook < glibc 2.34, __free_hook, callbacks en secciones .data personalizadas, páginas JIT).2. Abusar de primitivas de relative read para leak libc y realizar SROP/ROP into libc. 3. Inyectar un objeto compartido malicioso vía DT_RPATH/ LD_PRELOAD (si el entorno está controlado por el atacante) o ld_audit.4. Explotar format-string o una sobrescritura parcial de puntero para desviar el flujo de control sin tocar el GOT. |
💡 Incluso con Full RELRO la GOT of loaded shared libraries (e.g. libc itself) es only Partial RELRO porque esos objetos ya están mapeados cuando el loader aplica las relocations. Si consigues una primitiva de arbitrary write que pueda apuntar a las páginas de otro objeto compartido, todavía puedes pivotar la ejecución sobrescribiendo las entradas GOT de libc o la pila
__rtld_global, una técnica explotada regularmente en desafíos CTF modernos.
Real-world bypass example (2024 CTF – pwn.college “enlightened”)
El reto se entregó con Full RELRO. El exploit usó un off-by-one para corromper el tamaño de un chunk del heap, leaked libc con tcache poisoning, y finalmente sobrescribió __free_hook (fuera del segmento RELRO) con un one-gadget para obtener ejecución de código. No se requirió escribir en el GOT.
Investigación reciente & vulnerabilidades (2022-2025)
- glibc hook removal (2.34 → present) – los hooks de malloc/free fueron extraídos de la libc principal hacia el opcional
libc_malloc_debug.so, eliminando una primitiva común de bypass de Full‑RELRO; los exploits modernos deben apuntar a otros punteros escribibles. - GNU ld RELRO page‑alignment fix (binutils 2.39+/2.41) – el bug del linker 30612 causaba que los últimos bytes de
PT_GNU_RELROcompartieran una página escribible en sistemas con páginas de 64 KiB; los binutils actuales alinean RELRO amax-page-size, cerrando esa “RELRO gap”.
References
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


