このページの目次
ASLR とセキュリティ保護
カバー範囲: ASLR (stack/mmap/exec) → PIE → RELRO → stack canary → NX → CET (IBT/SHSTK) → CFI → セーフティチェックを防御として 適用対象: Linux ユーザーランド, GCC/Clang + glibc + kernel
概要
ASLR は Linux ユーザーランドにおけるセキュリティの最初の防衛線です。アドレス空間のレイアウトをランダム化することで、攻撃者がコード、データ、スタックの位置を予測できなくします。しかし、ASLR だけでは不十分です。PIE、RELRO、stack canary、NX はそれぞれ異なる攻撃面を解決し、組み合わせることで階層型防御を形成します。
ASLR の3層
# /proc/sys/kernel/randomize_va_space
# 0: 無効
# 1: mmap base + stack + vDSO のみをランダム化 (部分的)
# 2: 全ランダム化 + brk (完全, デフォルト)
# ランダム化される対象:
# - stack: 各 exec ごとにランダムオフセット (最大約8MBのランダム範囲)
# - mmap base: 共有ライブラリ .so および匿名 mmap の開始アドレス
# - executable: PIE (Position-Independent Executable) のみ
# - brk (heap): full モードでランダム化
# ASLR の影響を受けないもの:
# - 非 PIE 実行ファイル: 固定アドレス (0x400000)
# - vsyscall: 固定アドレス (0xffffffffff600000)
PIE: Position-Independent Executable
# 非 PIE (従来型, GCC デフォルトの -no-pie, 非推奨):
# .text が固定アドレス 0x400000 にロードされる
# → 攻撃者はすべての gadget のアドレスを知っている → ROP が容易
# PIE (現代のデフォルト, GCC -pie):
# 全体として .so のようにランダムロード可能 → ASLR が有効
# ロードベースアドレスは各 exec ごとにランダム
# ロードベースアドレスの確認:
|
# PIE のパフォーマンスコスト: 約2-3% (GOT の追加間接参照)
# -fno-plt オプションにより一部解消可能 (PLT をスキップし、GOT への直接アクセス)
RELRO: リロケーションの読み取り専用化
# リンカオプション:
# -Wl,-z,norelro: 保護なし
# -Wl,-z,relro: 部分的保護 (デフォルト)
# -Wl,-z,now: 完全保護 (BIND_NOW + RELRO)
# 効果:
# Partial RELRO:
# .got および .dynamic は起動後に読み取り専用になる
# .got.plt は依然として書き込み可能 (遅延バインディングが必要)
#
# Full RELRO (BIND_NOW):
# すべての GOT は起動後に読み取り専用になる
# コスト: すべてのシンボルが起動時に即時解決される → 起動が若干遅くなる
# 利点: GOT が書き込み不可 → GOT ポインターの上書きが不可能 → GOT overwrite 攻撃を防止
# 確認:
readelf -l /bin/ls | grep GNU_RELRO # 表示されれば → RELRO 有効
readelf -d /bin/ls | grep BIND_NOW # 表示されれば → Full RELRO
Stack Canary
// GCC -fstack-protector (自動有効)
// 各関数のスタックフレームの末尾に 8バイトの canary (ランダム値) を配置
// 関数終了時に canary をチェック → 変更されていれば → スタックオーバーフロー → abort
// Canary のソース:
// /proc/sys/kernel/randomize_va_space → AT_RANDOM (16バイト)
// または: __stack_chk_guard (glibc TLS, 起動時に AT_RANDOM から初期化)
// 確認:
// __stack_chk_fail() → __fortify_fail() → abort + ログ出力
// コンパイラレベル:
// -fstack-protector: char buf[8+] を持つ関数を保護
// -fstack-protector-strong: 配列やアドレス取得操作のある関数を保護 (推奨)
// -fstack-protector-all: すべての関数を保護 (パフォーマンスコスト約5%)
NX (No-Execute)
ハードウェア (x86: XD bit / NX bit, ARM: XN):
ELF セグメント PF_X → executable としてマッピング
スタックとヒープ: PF_X なし → 実行不可
→ 攻撃者はヒープ/スタックに shellcode を注入して実行にジャンプできない
→ ただし ROP は依然として有効 (既存の .text 断片を再利用)
確認:
cat /proc/cpuinfo | grep nx # x86: フラグに 'nx' が含まれる
readelf -l /bin/ls | grep STACK # PT_GNU_STACK: RW (X なし)
CET: コントロールフロー強制 (Intel 第11世代以降)
IBT (Indirect Branch Tracking):
ハードウェア: 間接ジャンプ(jmp/call [reg])は endbr32/endbr64 命令のみ許可
→ ROP/JOP が gadget の途中にジャンプ → CPU が検出 → #CP fault
SHSTK (Shadow Stack):
ハードウェア: 独立したシャドウスタックを維持 (通常の store では変更不可)
call: ハードウェアが戻りアドレスをシャドウスタックに push
ret: ハードウェアがシャドウスタックと通常スタックを比較 → 不一致 → #CP fault
→ ROP でスタックが変更 → ret 時に検出 → 拒否
コンパイラ: GCC 8+ / Clang 7+ -fcf-protection=full
カーネル: CET サポート (5.18以降)
複合防御
攻撃ベクトル 緩和策
────────────────────────────────────
スタックオーバーフロー → canary + NX + SHSTK
ヒープオーバーフロー → FORTIFY_SOURCE, ASAN (デバッグ用)
GOT overwrite → Full RELRO
ROP/JOP → ASLR + PIE + IBT
情報漏洩 → kptr_restrict, dmesg_restrict
UAF → ASAN (デバッグ用), SLAB_FREELIST_RANDOM
確認ツール
# バイナリのセキュリティ強化状態を確認
# または: hardening-check /bin/ls (Debian/Ubuntu devscripts)
# 出力例:
# Position Independent Executable: yes
# Stack protected: yes
# Fortify Source functions: yes
# Read-only relocations: yes
# Immediate binding: yes
参考文献
- カーネルドキュメント:
Documentation/admin-guide/sysctl/kernel.rst(randomize_va_space) - LWN: "CET on x86", "Full RELRO and BIND_NOW"
- ツール:
checksec,hardening-check,pwntools
キーワード: ASLR, PIE, RELRO, stack canary, NX, CET, IBT, SHSTK, FORTIFY_SOURCE, BIND_NOW