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 지원하기

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 relro when invoking ld directly).
  • 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 now forces 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_RELRO segment.
  • 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 nowGCC/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 levelTypical primitivePossible exploitation techniques
None / PartialArbitrary write1. Overwrite .got.plt entry and pivot execution.
2. ret2dlresolve – writable 세그먼트에 가짜 Elf64_Rela & Elf64_Sym를 생성하고 _dl_runtime_resolve를 호출.
3. .fini_array / atexit() 목록의 함수 포인터를 덮어쓰기.
FullGOT is read-only1. 다른 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 지원하기