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

RELRORelocation Read-Only의 약자로, 링커(ld)에 의해 구현된 완화 조치로, ELF의 데이터 세그먼트의 하위 집합을 모든 재배치가 적용된 후 읽기 전용으로 전환합니다. 목표는 공격자가 프로그램 실행 중에 역참조되는 GOT (Global Offset Table) 또는 기타 재배치 관련 테이블의 항목을 덮어쓰는 것을 방지하는 것입니다 (예: __fini_array).

현대 링커는 GOT (및 몇 가지 다른 섹션)을 .bss보다 앞서 위치시키고, 가장 중요한 것은 동적 로더가 재배치를 적용한 후 R–X로 다시 매핑되는 전용 PT_GNU_RELRO 세그먼트를 생성하여 RELRO를 구현합니다. 따라서 .bss에서의 전형적인 버퍼 오버플로우는 더 이상 GOT에 도달할 수 없으며, 임의 쓰기 원시를 사용하여 RELRO로 보호된 페이지 내의 함수 포인터를 덮어쓸 수 없습니다.

링커가 생성할 수 있는 보호 수준은 두 가지가 있습니다:

Partial RELRO

  • 플래그 -Wl,-z,relro (또는 ld를 직접 호출할 때 -z relro)로 생성됩니다.
  • GOT비-PLT 부분(데이터 재배치에 사용되는 부분)만 읽기 전용 세그먼트에 배치됩니다. 런타임에 수정해야 하는 섹션 – 가장 중요한 .got.plt지연 바인딩을 지원하며 여전히 쓰기가 가능합니다.
  • 이로 인해 임의 쓰기 원시가 PLT 항목을 덮어쓰거나 ret2dlresolve를 수행하여 실행 흐름을 리디렉션할 수 있습니다.
  • 성능 영향은 미미하며, 따라서 거의 모든 배포판이 수년간 최소 Partial RELRO로 패키지를 제공해왔습니다 (2016년부터 GCC/Binutils의 기본값입니다).

Full RELRO

  • 두 가지 플래그 -Wl,-z,relro,-z,now (일명 -z relro -z now)로 생성됩니다. -z now는 동적 로더가 모든 기호를 미리 해결하도록 강제하여 (즉각적 바인딩) .got.plt가 다시 쓰일 필요가 없고 안전하게 읽기 전용으로 매핑될 수 있도록 합니다.
  • 전체 GOT, .got.plt, .fini_array, .init_array, .preinit_array 및 몇 가지 추가 내부 glibc 테이블이 읽기 전용 PT_GNU_RELRO 세그먼트에 포함됩니다.
  • 측정 가능한 시작 오버헤드를 추가하지만 (모든 동적 재배치가 시작 시 처리됨) 런타임 오버헤드는 없습니다.

2023년부터 여러 주요 배포판이 기본적으로 Full RELRO로 시스템 툴 체인 (및 대부분의 패키지)을 컴파일하는 것으로 전환했습니다 – 예: Debian 12 “bookworm” (dpkg-buildflags 13.0.0)Fedora 35+. 따라서 펜테스터로서 모든 GOT 항목이 읽기 전용인 바이너리를 만날 것으로 예상해야 합니다.


How to Check the RELRO status of a binary

bash
$ 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에 의존하세요:

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

이진 파일이 실행 중인 경우(예: set-uid root 헬퍼), **/proc/$PID/exe**를 통해 실행 파일을 여전히 검사할 수 있습니다:

bash
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO

자신의 코드를 컴파일할 때 RELRO 활성화하기

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 nowGCC/clang( -Wl, 뒤에 전달됨)와 ld 모두에서 작동합니다. **CMake 3.18+**를 사용할 때는 내장 프리셋을 통해 전체 RELRO를 요청할 수 있습니다:

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")

우회 기술

RELRO 수준일반적인 원시가능한 악용 기술
없음 / 부분임의 쓰기1. .got.plt 항목을 덮어쓰고 실행을 전환합니다.
2. ret2dlresolve – 쓰기 가능한 세그먼트에서 가짜 Elf64_RelaElf64_Sym을 작성하고 _dl_runtime_resolve를 호출합니다.
3. .fini_array / atexit() 목록의 함수 포인터를 덮어씁니다.
전체GOT는 읽기 전용1. 다른 쓰기 가능한 코드 포인터 (C++ vtables, __malloc_hook < glibc 2.34, __free_hook, 사용자 정의 .data 섹션의 콜백, JIT 페이지)를 찾습니다.
2. 상대 읽기 원시를 악용하여 libc를 유출하고 SROP/ROP into libc를 수행합니다.
3. DT_RPATH/LD_PRELOAD를 통해 악성 공유 객체를 주입합니다 (환경이 공격자 제어 하에 있는 경우) 또는 ld_audit.
4. 형식 문자열 또는 부분 포인터 덮어쓰기를 악용하여 GOT에 손대지 않고 제어 흐름을 전환합니다.

💡 전체 RELRO가 있더라도 **로드된 공유 라이브러리의 GOT (예: libc 자체)**는 부분 RELRO만 있습니다. 왜냐하면 이러한 객체는 로더가 재배치를 적용할 때 이미 매핑되기 때문입니다. 임의 쓰기 원시를 얻으면 다른 공유 객체의 페이지를 대상으로 하여 libc의 GOT 항목이나 __rtld_global 스택을 덮어써서 실행을 전환할 수 있습니다. 이는 현대 CTF 챌린지에서 정기적으로 악용되는 기술입니다.

실제 우회 예시 (2024 CTF – pwn.college “enlightened”)

이 도전 과제는 전체 RELRO와 함께 제공되었습니다. 이 악용은 오프 바이 원을 사용하여 힙 청크의 크기를 손상시키고, tcache poisoning으로 libc를 유출한 후, __free_hook (RELRO 세그먼트 외부)을 원가젯으로 덮어써서 코드 실행을 얻었습니다. GOT 쓰기는 필요하지 않았습니다.


최근 연구 및 취약점 (2022-2025)

  • glibc 2.40에서 __malloc_hook / __free_hook 비권장 (2025) – 이러한 기호를 악용한 대부분의 현대 힙 악용은 이제 rtld_global._dl_load_jump 또는 C++ 예외 테이블과 같은 대체 벡터로 전환해야 합니다. 후크가 RELRO 외부에 존재하기 때문에 이들의 제거는 전체 RELRO 우회 난이도를 증가시킵니다.
  • Binutils 2.41 “최대 페이지 크기” 수정 (2024) – 버그로 인해 RELRO 세그먼트의 마지막 몇 바이트가 일부 ARM64 빌드에서 쓰기 가능한 데이터와 페이지를 공유할 수 있어 mprotect 이후에 쓸 수 있는 작은 RELRO 갭이 남았습니다. 업스트림은 이제 PT_GNU_RELRO를 페이지 경계에 정렬하여 해당 엣지 케이스를 제거합니다.

참조

  • Binutils 문서 – -z relro, -z nowPT_GNU_RELRO
  • “RELRO – 전체, 부분 및 우회 기술” – 블로그 게시물 @ wolfslittlered 2023

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