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는 Relocation 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
$ 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 헬퍼), **/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+**를 사용할 때는 내장 프리셋을 통해 전체 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 수준 | 일반적인 원시 | 가능한 악용 기술 |
---|---|---|
없음 / 부분 | 임의 쓰기 | 1. .got.plt 항목을 덮어쓰고 실행을 전환합니다. 2. ret2dlresolve – 쓰기 가능한 세그먼트에서 가짜 Elf64_Rela 및 Elf64_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 now
및PT_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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.