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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 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(및 몇몇 다른 섹션)를 **재정렬(re–ordering)**하여 이들이 .bss보다 앞에 위치하도록 만들고, 가장 중요한 것은 동적 로더가 재배치 적용을 완료한 직후 PT_GNU_RELRO 전용 세그먼트를 생성하여 R–X로 재매핑하는 방식으로 RELRO를 구현합니다. 결과적으로 .bss에서 발생하는 전형적인 버퍼 오버플로우는 더 이상 GOT에 도달할 수 없으며, arbitrary‐write 프리미티브로 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.
바이너리의 RELRO 상태를 확인하는 방법
$ 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
바이너리가 실행 중인 경우(예: a set-uid root helper), 실행 파일을 여전히 검사할 수 있습니다 via /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")
Bypass Techniques
| RELRO level | Typical primitive | Possible exploitation techniques |
|---|---|---|
| None / Partial | Arbitrary write | 1. Overwrite .got.plt entry and pivot execution. 2. ret2dlresolve – writable 세그먼트에 가짜 Elf64_Rela & Elf64_Sym를 생성하고 _dl_runtime_resolve를 호출.3. .fini_array / atexit() 목록의 함수 포인터를 덮어쓰기. |
| Full | GOT is read-only | 1. 다른 writable code pointers(C++ vtables, __malloc_hook < glibc 2.34, __free_hook, custom .data 섹션의 콜백, JIT 페이지)를 찾기.2. 상대적 읽기(relative read) 프리미티브를 악용해 libc를 leak하고 SROP/ROP into libc 수행. 3. 공격자가 제어하는 환경이라면 DT_RPATH/ LD_PRELOAD를 통해 악성 shared object를 주입하거나 ld_audit 사용.4. format-string 또는 부분 포인터 덮어쓰기를 이용해 GOT를 건드리지 않고 제어 흐름을 전환. |
💡 Full RELRO 환경에서도 **GOT of loaded shared libraries (e.g. libc itself)**는 로더가 이미 재배치를 적용할 때 맵핑되어 있기 때문에 only Partial RELRO입니다. 다른 shared object의 페이지를 대상으로 삼을 수 있는 arbitrary write 프리미티브를 얻으면 libc의 GOT 엔트리나
__rtld_global스택을 덮어써서 여전히 실행을 피벗할 수 있으며, 이는 현대 CTF 문제에서 자주 악용되는 기법입니다.
Real-world bypass example (2024 CTF – pwn.college “enlightened”)
해당 챌린지는 Full RELRO로 배포되었습니다. 익스플로잇은 off-by-one으로 힙 청크의 크기를 손상시켰고, tcache poisoning으로 libc를 leak한 뒤 RELRO 세그먼트 외부의 __free_hook을 one-gadget으로 덮어써 코드 실행을 얻었습니다. GOT write는 필요하지 않았습니다.
Recent research & vulnerabilities (2022-2025)
- glibc hook removal (2.34 → present) – malloc/free 훅들이 메인 libc에서 선택적
libc_malloc_debug.so로 분리되어 Full‑RELRO 우회에 자주 쓰이던 프리미티브가 제거되었습니다; 현대 익스플로잇은 다른 writable 포인터를 목표로 해야 합니다. - GNU ld RELRO page‑alignment fix (binutils 2.39+/2.41) – linker bug 30612는 64 KiB 페이지 시스템에서
PT_GNU_RELRO의 마지막 바이트가 쓰기 가능한 페이지를 공유하게 만들었고; 현재 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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


