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 より前に配置し直すことで RELRO を実装します。さらに重要なのは、動的ローダがリロケーションの適用を終えた直後に PT_GNU_RELRO セグメントを作成して R–X として再マップする点です。その結果、.bss** における典型的なバッファオーバーフローは GOT に到達できなくなり、RELRO 保護されたページ内の関数ポインタを上書きするために arbitrary‐write プリミティブを使うことができなくなります。

There are two levels of protection that the linker can emit:

Partial RELRO

  • フラグ -Wl,-z,relro(または -z relro を直接 ld に渡す場合)で生成されます。
  • GOTnon-PLT 部分(データリロケーションに使われる部分)のみが読み取り専用セグメントに入ります。実行時に変更が必要なセクション、特に lazy binding をサポートする .got.plt は書き込み可能なままです。
  • そのため、arbitrary write プリミティブは PLT エントリを上書きして実行フローを逸らす(あるいは ret2dlresolve を行う)ことがまだ可能です。
  • パフォーマンスへの影響はほとんど無く、そのため ほとんどのディストリビューションは何年も前から少なくとも Partial RELRO を有効にしたパッケージを出荷しています(2016 年時点での GCC/Binutils のデフォルトです)

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 セグメント内に収まります。
  • 起動時に処理される動的リロケーションが増えるため計測可能なスタートアップのオーバーヘッドがありますが、実行時のオーバーヘッドはありません

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 (part of pwntools and many distributions) parses ELF headers and prints the protection level. If you cannot use checksec, rely on 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 helper)の場合でも、実行可能ファイルは /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")

バイパス手法

RELRO レベル典型的なプリミティブ可能な悪用手法
None / PartialArbitrary write1. .got.plt のエントリを上書きして実行制御を奪う。
2. ret2dlresolve – writable セグメントに偽の Elf64_Rela & Elf64_Sym を作成し、_dl_runtime_resolve を呼ぶ。
3. .fini_array / atexit() リスト内の関数ポインタを上書きする。
FullGOT は読み取り専用1. 他の書き込み可能なコードポインタ(C++ vtables, __malloc_hook < glibc 2.34, __free_hook, カスタム .data セクション内のコールバック, JIT ページ)を探す。
2. 相対的な読み取り (relative read) プリミティブを悪用して libc を leak し、SROP/ROP into libc を実行する。
3. (環境が攻撃者に制御されている場合) DT_RPATH/LD_PRELOAD 経由で不正な共有オブジェクトを注入する、あるいは ld_audit を利用する。
4. format-string や部分ポインタ上書きを悪用して GOT に触れずに制御フローを逸らす。

💡 Full RELRO であっても GOT of loaded shared libraries (e.g. libc itself) は、ローダが再配置を適用する時点で既にマップされているため only Partial RELRO です。もし他の共有オブジェクトのページをターゲットにできる arbitrary write プリミティブを獲得すれば、libc の GOT エントリや __rtld_global スタックを上書きして実行をピボットさせることができ、これは現代の CTF チャレンジで頻繁に悪用されるテクニックです。

実世界のバイパス例(2024 CTF – pwn.college “enlightened”

当該チャレンジは Full RELRO で配布されていました。エクスプロイトは off-by-one を用いてヒープチャンクのサイズを破壊し、tcache poisoning で libc を leak し、最終的に __free_hook(RELRO セグメント外)を one-gadget に上書きしてコード実行を得ました。GOT の書き換えは不要でした。


最近の研究と脆弱性(2022–2025)

  • glibc hook removal (2.34 → present) – malloc/free hooks は main libc からオプションの libc_malloc_debug.so に移され、一般的な Full‑RELRO バイパスのプリミティブが排除されました。現代のエクスプロイトは他の書き込み可能なポインタを狙う必要があります。
  • 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をサポートする