Relro
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
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).
现代链接器通过重新排列 GOT(以及其他一些节),使其位于 .bss 之前,并——最重要的——创建一个专门的 PT_GNU_RELRO 段,该段在动态加载器完成重定位后立即被映射为 R–X。因此,位于 .bss 的典型 buffer overflows 无法再到达 GOT,arbitrary‐write primitives 也不能用于覆盖位于 RELRO 保护页内的函数指针。
There are two levels of protection that the linker can emit:
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 (是 pwntools 的一部分,并包含在许多发行版中) 解析 ELF 头并打印保护级别。 如果你无法使用 checksec,请使用 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
如果二进制正在运行(例如 set-uid root helper),你仍然可以通过 /proc/$PID/exe 检查可执行文件:
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
在编译你自己的代码时启用 RELRO
# 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 对 GCC/clang(在 -Wl, 之后传递)和直接对 ld 都有效。 当使用 CMake 3.18+ 时,你可以使用内置预设请求 Full RELRO:
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")
绕过技巧
| RELRO level | 典型原语 | 可能的利用技术 |
|---|---|---|
| None / Partial | Arbitrary write | 1. 覆盖 .got.plt 条目并 pivot execution。 2. ret2dlresolve – 在可写段中伪造 Elf64_Rela & Elf64_Sym 并调用 _dl_runtime_resolve。3. 覆盖 .fini_array / atexit() 列表中的函数指针。 |
| Full | GOT 是只读的 | 1. 寻找 其他可写的代码指针 (C++ vtables, __malloc_hook < glibc 2.34, __free_hook, callbacks in custom .data sections, JIT pages)。2. 滥用 relative read 原语来 leak libc 并执行 SROP/ROP into libc。 3. 通过 DT_RPATH/ LD_PRELOAD(如果环境受攻击者控制)或 ld_audit 注入恶意共享对象。4. 利用 format-string 或 partial pointer overwrite 改变控制流而无需触碰 GOT。 |
💡 即便在 Full RELRO 下,**已加载共享库的 GOT(例如 libc 本身)**仍然是 only Partial RELRO,因为这些对象在 loader 应用重定位时已经被映射。如果你获得了能写入其他共享对象页面的 arbitrary write 原语,你仍然可以通过覆盖 libc 的 GOT 条目或
__rtld_global栈来 pivot execution,这是一种在现代 CTF 挑战中经常被利用的技术。
真实世界绕过示例 (2024 CTF – pwn.college “enlightened”)
该题目带有 Full RELRO。利用链使用了一个 off-by-one 破坏了堆 chunk 的大小,通过 tcache poisoning leak 了 libc,最后用 one-gadget 覆盖了位于 RELRO 段之外的 __free_hook 以获得代码执行。无需写入 GOT。
近期研究与漏洞 (2022-2025)
- glibc hook removal (2.34 → present) – malloc/free hooks 从主 libc 中被抽离到可选的
libc_malloc_debug.so,消除了一个常见的 Full‑RELRO 绕过原语;现代利用必须针对其他可写指针。 - GNU ld RELRO page‑alignment fix (binutils 2.39+/2.41) – linker bug 30612 导致
PT_GNU_RELRO的末尾字节在 64 KiB 页面系统上与一个可写页面共享;当前的 binutils 将 RELRO 对齐到max-page-size,修补了该 “RELRO gap”。
References
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。


