Relro
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Relro
RELRO stands for Relocation Read-Only and it is a mitigation implemented by the linker (ld) that turns a subset of the ELF’s data segments read-only after all relocations have been applied. The goal is to stop an attacker from overwriting entries in the GOT (Global Offset Table) or other relocation-related tables that are dereferenced during program execution (e.g. __fini_array).
I linker moderni implementano RELRO riordinando la GOT (e alcune altre sezioni) in modo che si trovino prima della .bss e — cosa più importante — creando un segmento dedicato PT_GNU_RELRO che viene rimappato R–X subito dopo che il loader dinamico ha finito di applicare le relocazioni. Di conseguenza, i tipici buffer overflow nella .bss non possono più raggiungere la GOT e le primitive di scrittura arbitraria non possono essere usate per sovrascrivere i puntatori a funzione che risiedono all’interno di una pagina protetta da RELRO.
Ci sono due livelli di protezione che il linker può emettere:
Partial RELRO
- Prodotto con il flag
-Wl,-z,relro(o semplicemente-z relroquando si invocalddirettamente). - Solo la parte non-PLT della GOT (la parte usata per le relocazioni dati) viene inserita nel segmento sola-lettura. Le sezioni che devono essere modificate a run-time – soprattutto .got.plt che supporta il lazy binding – rimangono scrivibili.
- Per questo motivo, una primitiva di scrittura arbitraria può ancora reindirizzare il flusso di esecuzione sovrascrivendo una voce PLT (o eseguendo ret2dlresolve).
- L’impatto sulle prestazioni è trascurabile e quindi quasi tutte le distributioni distribuiscono pacchetti con almeno Partial RELRO da anni (è il default di GCC/Binutils dal 2016).
Full RELRO
- Prodotto con entrambi i flag
-Wl,-z,relro,-z,now(a.k.a.-z relro -z now).-z nowforza il loader dinamico a risolvere tutti i simboli all’avvio (eager binding) in modo che .got.plt non debba più essere scritto e possa essere mappato in sola-lettura in sicurezza. - L’intera GOT, .got.plt, .fini_array, .init_array, .preinit_array e alcune tabelle interne aggiuntive di glibc finiscono all’interno di un segmento
PT_GNU_RELROmappato in sola-lettura. - Aggiunge un sovraccarico misurabile allo start-up (tutte le relocazioni dinamiche vengono processate al lancio) ma nessun overhead a run-time.
Dal 2023 diverse distributioni mainstream hanno iniziato a compilare la system tool-chain (e la maggior parte dei pacchetti) con Full RELRO by default – ad es. Debian 12 “bookworm” (dpkg-buildflags 13.0.0) e Fedora 35+. Come pentester dovresti quindi aspettarti di incontrare binari in cui ogni voce della GOT è in sola-lettura.
Come verificare lo stato RELRO di un binario
$ checksec --file ./vuln
[*] '/tmp/vuln'
Arch: amd64-64-little
RELRO: Full
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
checksec (parte di pwntools e molte distribuzioni) analizza le intestazioni ELF e stampa il livello di protezione. Se non puoi usare checksec, affidati a 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
Se il binario è in esecuzione (ad es. un set-uid root helper), puoi comunque ispezionare l’eseguibile via /proc/$PID/exe:
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
Abilitare RELRO quando si compila il proprio codice
# 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 funziona sia con GCC/clang (passato dopo -Wl,) sia con ld direttamente. Quando si usa CMake 3.18+ puoi richiedere Full RELRO con il preset integrato:
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")
Tecniche di bypass
| RELRO level | Typical primitive | Possible exploitation techniques |
|---|---|---|
| None / Partial | Arbitrary write | 1. Sovrascrivere la voce .got.plt e pivotare l’esecuzione. 2. ret2dlresolve – creare finti Elf64_Rela & Elf64_Sym in un segmento scrivibile e chiamare _dl_runtime_resolve.3. Sovrascrivere puntatori a funzione in .fini_array / lista atexit(). |
| Full | GOT is read-only | 1. Cercare altri puntatori di codice scrivibili (C++ vtables, __malloc_hook < glibc 2.34, __free_hook, callbacks in custom .data sections, JIT pages).2. Abusare di primitive relative read per effettuare un leak di libc ed eseguire SROP/ROP into libc. 3. Iniettare un shared object malevolo via DT_RPATH/ LD_PRELOAD (se l’ambiente è controllato dall’attaccante) o ld_audit.4. Sfruttare format-string o partial pointer overwrite per deviare il controllo del flusso senza toccare la GOT. |
💡 Anche con Full RELRO la GOT delle shared libraries caricate (p.es. libc stessa) è solo Partial RELRO perché quegli oggetti sono già mappati quando il loader applica le relocazioni. Se ottieni una primitiva di arbitrary write che può puntare alle pagine di un altro shared object puoi comunque pivotare l’esecuzione sovrascrivendo le voci GOT di libc o lo stack
__rtld_global, una tecnica regolarmente sfruttata nelle moderne challenge CTF.
Esempio reale di bypass (2024 CTF – pwn.college “enlightened”)
La challenge è stata fornita con Full RELRO. L’exploit ha usato un off-by-one per corrompere la size di un heap chunk, ha leakato libc con tcache poisoning, e alla fine ha sovrascritto __free_hook (fuori dal segmento RELRO) con un one-gadget per ottenere esecuzione di codice. Non è stata necessaria alcuna scrittura sulla GOT.
Ricerche recenti e vulnerabilità (2022-2025)
- glibc hook removal (2.34 → present) – i malloc/free hooks sono stati estratti dal main libc in
libc_malloc_debug.soopzionale, eliminando una comune primitiva di bypass per Full‑RELRO; gli exploit moderni devono mirare ad altri puntatori scrivibili. - GNU ld RELRO page‑alignment fix (binutils 2.39+/2.41) – il linker bug 30612 faceva sì che gli ultimi byte di
PT_GNU_RELROcondividessero una pagina scrivibile sui sistemi con pagine da 64 KiB; le versioni attuali di binutils allineano RELRO amax-page-size, chiudendo quel “RELRO gap”.
Riferimenti
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.


