# Relro
{{#include ../../banners/hacktricks-training.md}}
## Relro
**RELRO** steht für **Relocation Read-Only** und ist eine Minderung, die vom Linker (`ld`) implementiert wird, der einen Teil der ELF-Datensegmente **nach Anwendung aller Relokationen schreibgeschützt macht**. Das Ziel ist es, einen Angreifer daran zu hindern, Einträge in der **GOT (Global Offset Table)** oder anderen relokationsbezogenen Tabellen zu überschreiben, die während der Programmausführung dereferenziert werden (z. B. `__fini_array`).
Moderne Linker implementieren RELRO, indem sie die **GOT** (und einige andere Abschnitte) **neu anordnen**, sodass sie **vor** der **.bss** leben und – am wichtigsten – indem sie ein dediziertes `PT_GNU_RELRO`-Segment erstellen, das direkt nach Abschluss der Relokationen durch den dynamischen Loader `R–X` umgemappt wird. Folglich können typische Pufferüberläufe in der **.bss** die GOT nicht mehr erreichen, und willkürliche Schreibprimitive können nicht verwendet werden, um Funktionszeiger zu überschreiben, die sich innerhalb einer RELRO-geschützten Seite befinden.
Es gibt **zwei Ebenen** des Schutzes, die der Linker ausgeben kann:
### Partial RELRO
* Produziert mit dem Flag `-Wl,-z,relro` (oder einfach `-z relro`, wenn `ld` direkt aufgerufen wird).
* Nur der **nicht-PLT** Teil der **GOT** (der Teil, der für Datenrelokationen verwendet wird) wird in das schreibgeschützte Segment gelegt. Abschnitte, die zur Laufzeit geändert werden müssen – am wichtigsten **.got.plt**, das **lazy binding** unterstützt – bleiben beschreibbar.
* Aufgrund dessen kann ein **willkürliches Schreiben** immer noch den Ausführungsfluss umleiten, indem ein PLT-Eintrag überschrieben wird (oder durch Ausführen von **ret2dlresolve**).
* Der Leistungsimpact ist vernachlässigbar und daher **versenden fast alle Distributionen seit Jahren Pakete mit mindestens Partial RELRO (es ist der GCC/Binutils-Standard seit 2016)**.
### Full RELRO
* Produziert mit **beiden** Flags `-Wl,-z,relro,-z,now` (auch bekannt als `-z relro -z now`). `-z now` zwingt den dynamischen Loader, **alle** Symbole im Voraus aufzulösen (eager binding), sodass **.got.plt** nie wieder geschrieben werden muss und sicher schreibgeschützt gemappt werden kann.
* Die gesamte **GOT**, **.got.plt**, **.fini_array**, **.init_array**, **.preinit_array** und einige zusätzliche interne glibc-Tabellen landen in einem schreibgeschützten `PT_GNU_RELRO`-Segment.
* Fügt messbare Startkosten hinzu (alle dynamischen Relokationen werden beim Start verarbeitet), aber **keine Laufzeitkosten**.
Seit 2023 haben mehrere gängige Distributionen damit begonnen, die **System-Toolchain** (und die meisten Pakete) standardmäßig mit **Full RELRO** zu kompilieren – z. B. **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** und **Fedora 35+**. Als Pentester sollten Sie daher erwarten, auf Binärdateien zu stoßen, bei denen **jeder GOT-Eintrag schreibgeschützt ist**.
---
## So überprüfen Sie den RELRO-Status einer Binärdatei
```bash
$ checksec --file ./vuln
[*] '/tmp/vuln'
Arch: amd64-64-little
RELRO: Full
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
```
`checksec` (Teil von [pwntools](https://github.com/pwncollege/pwntools) und vielen Distributionen) analysiert `ELF`-Header und gibt das Schutzniveau aus. Wenn Sie `checksec` nicht verwenden können, verlassen Sie sich auf `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
```
Wenn die Binärdatei läuft (z. B. ein set-uid root Helper), können Sie die ausführbare Datei weiterhin **über `/proc/$PID/exe`** inspizieren:
```bash
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
```
---
## Aktivieren von RELRO beim Kompilieren Ihres eigenen Codes
```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` funktioniert sowohl für **GCC/clang** (nach `-Wl,` übergeben) als auch direkt für **ld**. Wenn Sie **CMake 3.18+** verwenden, können Sie Full RELRO mit dem integrierten Preset anfordern:
```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")
```
---
## Bypass-Techniken
| RELRO-Stufe | Typische Primitive | Mögliche Ausnutzungstechniken |
|-------------|-------------------|----------------------------------|
| Keine / Teilweise | Arbiträres Schreiben | 1. Überschreiben des **.got.plt**-Eintrags und Ausführung umschalten.
2. **ret2dlresolve** – gefälschte `Elf64_Rela` & `Elf64_Sym` in einem beschreibbaren Segment erstellen und `_dl_runtime_resolve` aufrufen.
3. Funktionszeiger in **.fini_array** / **atexit()**-Liste überschreiben. |
| Voll | GOT ist schreibgeschützt | 1. Nach **anderen beschreibbaren Codezeigern** suchen (C++ vtables, `__malloc_hook` < glibc 2.34, `__free_hook`, Rückrufe in benutzerdefinierten `.data`-Sektionen, JIT-Seiten).
2. Missbrauch von *relativen Lese*-Primitiven, um libc auszulesen und **SROP/ROP in libc** durchzuführen.
3. Ein bösartiges Shared Object über **DT_RPATH**/`LD_PRELOAD` injizieren (wenn die Umgebung vom Angreifer kontrolliert wird) oder **`ld_audit`**.
4. **Format-String** oder teilweise Zeigerüberschreibung ausnutzen, um den Kontrollfluss umzuleiten, ohne die GOT zu berühren. |
> 💡 Selbst mit Voll-RELRO ist die **GOT von geladenen Shared Libraries (z.B. libc selbst)** **nur Teilweise RELRO**, da diese Objekte bereits gemappt sind, wenn der Loader die Relokationen anwendet. Wenn Sie ein **arbiträres Schreiben**-Primitive erhalten, das auf die Seiten eines anderen Shared Objects abzielt, können Sie die Ausführung weiterhin umschalten, indem Sie die GOT-Einträge von libc oder den `__rtld_global`-Stack überschreiben, eine Technik, die regelmäßig in modernen CTF-Herausforderungen ausgenutzt wird.
### Beispiel für einen echten Bypass (2024 CTF – *pwn.college “enlightened”*)
Die Herausforderung wurde mit Voll-RELRO ausgeliefert. Der Exploit nutzte ein **Off-by-One**, um die Größe eines Heap-Chunks zu korrumpieren, leakte libc mit `tcache poisoning` und überschreibt schließlich `__free_hook` (außerhalb des RELRO-Segments) mit einem One-Gadget, um Codeausführung zu erhalten. Es war kein GOT-Schreiben erforderlich.
---
## Aktuelle Forschung & Schwachstellen (2022-2025)
* **glibc 2.40 deprecates `__malloc_hook` / `__free_hook` (2025)** – Die meisten modernen Heap-Exploits, die diese Symbole ausnutzten, müssen nun auf alternative Vektoren wie **`rtld_global._dl_load_jump`** oder C++-Ausnahmetabellen umschalten. Da Hooks **außerhalb** von RELRO leben, erhöht ihre Entfernung die Schwierigkeit von Voll-RELRO-Bypässen.
* **Binutils 2.41 “max-page-size” Fix (2024)** – Ein Fehler erlaubte es, dass die letzten paar Bytes des RELRO-Segments eine Seite mit beschreibbaren Daten auf einigen ARM64-Bauten teilten, was eine kleine **RELRO-Lücke** hinterließ, die nach `mprotect` geschrieben werden konnte. Der Upstream richtet jetzt `PT_GNU_RELRO` an Seitengrenzen aus, wodurch diesen Randfall beseitigt wird.
---
## Referenzen
* Binutils-Dokumentation – *`-z relro`, `-z now` und `PT_GNU_RELRO`*
* *“RELRO – Voll, Teilweise und Bypass-Techniken”* – Blogbeitrag @ wolfslittlered 2023
{{#include ../../banners/hacktricks-training.md}}