Relro
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Relro
RELRO stands for Relocation Read-Only i jest to mitigacja zaimplementowana przez linker (ld), która ustawia podzbiór segmentów danych ELF jako tylko do odczytu po zastosowaniu wszystkich relokacji. Celem jest uniemożliwienie atakującemu nadpisania wpisów w GOT (Global Offset Table) lub innych tabel związanych z relokacjami, które są dereferencjonowane podczas wykonywania programu (np. __fini_array).
Nowoczesne linkery implementują RELRO poprzez ponowne uporządkowanie GOT (i kilku innych sekcji), tak aby znajdowały się przed .bss i — co najważniejsze — poprzez utworzenie dedykowanego segmentu PT_GNU_RELRO, który jest remapowany jako R–X zaraz po tym, jak dynamic loader zakończy stosowanie relokacji. W konsekwencji typowe buffer overflows w .bss nie mogą już dotrzeć do GOT, a arbitrary‐write primitives nie mogą być użyte do nadpisania wskaźników funkcji znajdujących się na stronie chronionej przez RELRO.
Istnieją dwa poziomy ochrony, które linker może wygenerować:
Partial RELRO
- Produced with the flag
-Wl,-z,relro(or just-z relrowhen invokinglddirectly). - Only the non-PLT part of the GOT (the part used for data relocations) is put into the read-only segment. Sections that need to be modified at run-time – most importantly .got.plt which supports lazy binding – remain writable.
- Because of that, an arbitrary write primitive can still redirect execution flow by overwriting a PLT entry (or by performing ret2dlresolve).
- The performance impact is negligible and therefore almost every distribution has been shipping packages with at least Partial RELRO for years (it is the GCC/Binutils default as of 2016).
Full RELRO
- Produced with both flags
-Wl,-z,relro,-z,now(a.k.a.-z relro -z now).-z nowforces the dynamic loader to resolve all symbols up-front (eager binding) so that .got.plt never needs to be written again and can safely be mapped read-only. - The entire GOT, .got.plt, .fini_array, .init_array, .preinit_array and a few additional internal glibc tables end up inside a read-only
PT_GNU_RELROsegment. - Adds measurable start-up overhead (all dynamic relocations are processed at launch) but no run-time overhead.
Since 2023 several mainstream distributions have switched to compiling the system tool-chain (and most packages) with Full RELRO by default – e.g. Debian 12 “bookworm” (dpkg-buildflags 13.0.0) and Fedora 35+. As a pentester you should therefore expect to encounter binaries where every GOT entry is read-only.
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 (część pwntools i wielu dystrybucji) analizuje nagłówki ELF i wypisuje poziom ochrony. Jeśli nie możesz użyć checksec, polegaj na 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
Jeśli binary jest uruchomiony (np. set-uid root helper), nadal możesz zbadać plik wykonywalny przez /proc/$PID/exe:
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
Włączanie RELRO podczas kompilacji własnego kodu
# 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 działa zarówno dla GCC/clang (przekazywane po -Wl,) jak i bezpośrednio dla ld. Przy użyciu CMake 3.18+ możesz wymusić Full RELRO za pomocą wbudowanego presetu:
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")
Techniki obejścia
| RELRO level | Typical primitive | Possible exploitation techniques |
|---|---|---|
| None / Partial | Arbitrary write | 1. Nadpisz .got.plt entry i przekieruj wykonanie. 2. ret2dlresolve – stwórz fałszywe Elf64_Rela & Elf64_Sym w zapisywalnym segmencie i wywołaj _dl_runtime_resolve.3. Nadpisz wskaźniki funkcji w .fini_array / liście atexit(). |
| Full | GOT is read-only | 1. Szukaj other writable code pointers (C++ vtables, __malloc_hook < glibc 2.34, __free_hook, callbacki w niestandardowych sekcjach .data, strony JIT).2. Wykorzystaj prymitywy relative read do leak libc i przeprowadź SROP/ROP into libc. 3. Wstrzyknij złośliwy shared object przez DT_RPATH/ LD_PRELOAD (jeśli środowisko jest kontrolowane przez atakującego) lub ld_audit.4. Wykorzystaj format-string lub częściowe nadpisanie wskaźnika, aby przekierować przepływ sterowania bez dotykania GOT. |
💡 Nawet przy Full RELRO GOT załadowanych bibliotek współdzielonych (np. libc) jest tylko Partial RELRO, ponieważ te obiekty są już zmapowane, gdy loader stosuje relokacje. Jeśli zdobędziesz prymityw arbitrary write, który może celować w strony innego obiektu współdzielonego, nadal możesz przekierować wykonanie przez nadpisanie wpisów GOT libc lub stosu
__rtld_global— technika regularnie wykorzystywana w nowoczesnych zadaniach CTF.
Real-world bypass example (2024 CTF – pwn.college “enlightened”)
The challenge shipped with Full RELRO. The exploit used an off-by-one to corrupt the size of a heap chunk, leaked libc with tcache poisoning, and finally overwrote __free_hook (outside of the RELRO segment) with a one-gadget to get code execution. No GOT write was required.
Najnowsze badania i luki (2022-2025)
- glibc hook removal (2.34 → present) – malloc/free hooks zostały przeniesione z głównego libc do opcjonalnego
libc_malloc_debug.so, eliminując powszechny prymityw obejścia Full‑RELRO; współczesne exploity muszą celować w inne zapisywalne wskaźniki. - GNU ld RELRO page‑alignment fix (binutils 2.39+/2.41) – linker bug 30612 powodował, że ostatnie bajty
PT_GNU_RELROdzieliły zapisywalną stronę w systemach ze stroną 64 KiB; obecne binutils wyrównują RELRO domax-page-size, zamykając tę „RELRO gap”.
References
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.


