Relro
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Relro
RELRO означає Relocation Read-Only і є заходом, реалізованим компоновником (ld
), який перетворює підмножину сегментів даних ELF на тільки для читання після застосування всіх перенесень. Мета полягає в тому, щоб зупинити зловмисника від перезапису записів у GOT (Global Offset Table) або інших таблицях, пов'язаних з перенесеннями, які розіменовуються під час виконання програми (наприклад, __fini_array
).
Сучасні компоновники реалізують RELRO, переставляючи GOT (і кілька інших секцій) так, щоб вони знаходилися перед .bss і – що найважливіше – створюючи спеціальний сегмент PT_GNU_RELRO
, який перенаправляється R–X
відразу після того, як динамічний завантажувач закінчує застосування перенесень. Внаслідок цього типові переповнення буфера в .bss більше не можуть досягти GOT, і примітиви довільного запису не можуть бути використані для перезапису вказівників функцій, які знаходяться всередині захищеної сторінки RELRO.
Існує два рівні захисту, які може випустити компоновник:
Partial RELRO
- Виробляється з прапором
-Wl,-z,relro
(або просто-z relro
, коли викликаєтьсяld
безпосередньо). - Тільки не-PLT частина GOT (частина, що використовується для перенесень даних) поміщається в сегмент тільки для читання. Секції, які потрібно змінювати під час виконання – найважливіше .got.plt, що підтримує lazy binding – залишаються записуваними.
- Через це примітив довільного запису все ще може перенаправити потік виконання, перезаписуючи запис PLT (або виконуючи ret2dlresolve).
- Вплив на продуктивність незначний, тому майже кожен дистрибутив протягом багатьох років постачає пакунки з принаймні Partial RELRO (це стандарт GCC/Binutils з 2016 року).
Full RELRO
- Виробляється з обома прапорами
-Wl,-z,relro,-z,now
(також відомий як-z relro -z now
).-z now
змушує динамічний завантажувач вирішувати всі символи заздалегідь (eager binding), щоб .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 є тільки для читання.
Як перевірити статус 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
Якщо двійковий файл працює (наприклад, допоміжна програма з 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 у libc. 3. Впровадити зловмисний спільний об'єкт через DT_RPATH/ LD_PRELOAD (якщо середовище контролюється атакуючим) або ld_audit .4. Використати форматний рядок або частковий перезапис вказівника для зміни потоку управління без торкання GOT. |
💡 Навіть з Повним RELRO GOT завантажених спільних бібліотек (наприклад, сама libc) є тільки Частковим RELRO, оскільки ці об'єкти вже відображені, коли завантажувач застосовує перенесення. Якщо ви отримали довільний запис примітив, який може націлюватися на сторінки іншого спільного об'єкта, ви все ще можете змінити виконання, перезаписавши записи GOT libc або стек
__rtld_global
, техніка, яка регулярно використовується в сучасних CTF-завданнях.
Приклад обходу в реальному світі (2024 CTF – pwn.college “enlightened”)
Завдання постачалося з Повним RELRO. Експлуатація використовувала off-by-one для корупції розміру частини купи, витекла libc з tcache poisoning
, і нарешті перезаписала __free_hook
(поза сегментом RELRO) з одним гаджетом для отримання виконання коду. Запис у GOT не був потрібен.
Останні дослідження та вразливості (2022-2025)
- glibc 2.40 знецінює
__malloc_hook
/__free_hook
(2025) – Більшість сучасних експлуатацій купи, які зловживали цими символами, тепер повинні переходити на альтернативні вектори, такі якrtld_global._dl_load_jump
або таблиці виключень C++. Оскільки гачки живуть поза RELRO, їх видалення ускладнює обходи Повного RELRO. - Виправлення “max-page-size” Binutils 2.41 (2024) – Помилка дозволила останнім кільком байтам сегмента RELRO ділити сторінку з записуваними даними на деяких збірках ARM64, залишаючи маленький RELRO-проміжок, який можна було записати після
mprotect
. Тепер upstream вирівнюєPT_GNU_RELRO
до меж сторінок, усуваючи цей крайній випадок.
Посилання
- Документація Binutils –
-z relro
,-z now
таPT_GNU_RELRO
- “RELRO – Повний, Частковий та Техніки обходу” – допис у блозі @ wolfslittlered 2023
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.