PR 本記事には広告(Amazonアソシエイト・もしもアフィリエイト・A8.net等)が含まれます。掲載情報の正確性には努めていますが、商品の詳細は必ずリンク先で最新情報をご確認ください。
CTF・セキュリティ学習

【保護機構と回避編】Canary・PIE・ASLR・RELROの戦い方|CTF思考フレームワーク #65

かも次郎とアンペンが「保護機構と回避」を解説するマスコットイラスト
安全に生きたい編集部

こんにちは、アンペンです!

今回は保護機構と回避編。これまでBoF・ROP・FSB・Heap攻撃と扱ってきましたが、攻撃側の発展に伴いOS・コンパイラ・CPUは多層の保護機構を積み上げてきましたchecksec1コマンドで4項目(Canary・NX・PIE/ASLR・RELRO)を確認できる現代では、各保護の『何を守るか』『どう突破されるか』を覚えるのが攻防両面で必須です。

さらにIntel CET(Shadow Stack/IBT)、ARM PAC/MTE、Hardened allocator(Scudo/GWP-ASan)など新世代のHW・OS保護も実用フェーズに入っており、古典攻撃は『そもそも成立しない』未来が見えてきました。

保護機構の話は、つい『専門用語の暗記大会』になりがちですが、見方を変えると面白いんです。攻める側にとっては『どの壁が立っているか』を最初に確認する偵察、守る側にとっては『どの壁を立てるか』の設計図。同じ4項目を、攻守どちらの目でも読める——それがこの回のテーマです。checksec の出力を“作戦ボード”として読めるようになりましょう。

checksecで全部緑なら絶対安全?

絶対ではない。各保護は『リーク→計算→上書き』を1段ずつ重くする壁。Canaryはリーク経路、PIE/ASLRはアドレスリーク、RELRO Fullなら__free_hook等のhookか_IO_FILE系を狙う応用へ。1層ずつ崩していく発想を持とう。

まず結論

4大保護機構の役割と典型突破口:(1)Stack Canary(BoFでretを書き換えると検証値ズレで検出→%p等でcanary値リークすれば回避)、(2)NX/DEP(データ領域実行禁止→ROPで既存命令の継ぎ接ぎ)、(3)ASLR/PIE(コード/libcアドレスをランダム化→libcリーク+offset計算 or 部分上書き(下位12bit固定の性質)で回避)、(4)RELRO Full(GOT書込禁止→__free_hook(glibc <2.34)/_IO_FILE系を狙う応用へ)。すべてON+CET-IBT+Shadow Stackが現代のベストプラクティス。

この記事で分かること

  • 4大保護機構それぞれの仕組みと守る範囲
  • checksec出力の正しい読み方
  • 各保護の典型的な回避手筋
  • 新世代保護(CET / PAC / MTE / Scudo)の役割
  • ビルド時推奨フラグまとめ
難易度:中〜上級 所要時間:13分 体験:checksec読み+突破口決定 おすすめ:#64の後

📖 はじめてのWebセキュリティ #65|保護機構と回避編
Canary・PIE・ASLR・RELROの戦い方。 シリーズ一覧を見る →

⚠️ 大事なお約束
本番システムへのexploitは違法。CTF・自作バイナリのみで確認してください。

4大保護機構の役割を1枚で把握

  • Stack Canary:関数entry時にスタックに『見張り番』値(fs:0x28から読む)を置き、ret前に検証。BoFで戻りアドレスまで上書きするとcanaryもズレるためabortできる。-fstack-protector-strongで有効化
  • NX/DEP(W^X):同じページに書き込み権限と実行権限を同時付与しない。スタック・ヒープ上のshellcode実行を不可能にする
  • ASLR/PIE:カーネルが各実行でlibc/コード/スタック/ヒープのベースアドレスをランダム化。アドレス推測攻撃を困難に。PIE(Position Independent Executable)で本体バイナリもランダム化対象に
  • RELRO(Partial/Full):Partialは『起動時にGOTを書込禁止だが遅延解決領域は書込可』、Fullは『起動時に全関数を解決し全GOTを読取専用化』。GOT Overwriteの根を断つ

ここで大事な考え方を一つ。これらの保護は、単体で『絶対に破られない壁』ではありません。それぞれは『攻撃を一段ずつ重くする壁』です。Canaryがあればリークの一手間が増え、ASLRがあればアドレス特定の一手間が増える。一枚一枚は乗り越えられても、重ねるほど攻撃の手数とコストが跳ね上がる。“完璧な壁”ではなく“面倒くささの積み増し”——この発想が、多層防御の本質なんです。

図解:checksecの読み方と各層の突破口

checksec --file=./vulnで一目で確認できる4項目について、『どの状態でどう突破できるか』を頭に入れておくと、CTF問題開いた瞬間に作戦が立ちます。

Canary・NX・ASLR/PIE・RELROのchecksec4項目と典型的な突破口を表でまとめた図解
図1:checksec4項目と典型的な突破口
🏰 たとえるなら、城の防御4層

Canaryは『城門の見張り番』、NXは『城内の武器庫封印』、ASLR/PIEは『城内地図のランダム配置』、RELRO Fullは『金庫を起動後に固定して触らせない』。城を落とすには見張りを買収(canaryリーク)→武器庫を諦めて既存武器を使う(ROP)→ランダム地図を入手(アドレスリーク)→金庫が無理なら使用人を脅す(__free_hook書換)層を1つずつ崩していくのが定石。すべての層が完璧に機能していれば、攻撃者には『不可能ではないが現実的に困難』な状況を作れます。

ここで覚える用語:部分上書き(Partial Overwrite)
ASLRはページ境界(0x1000バイト=12bit)単位でランダム化されるため、下位12ビットはASLRに影響されない固定値です。これを使い、戻りアドレスや関数ポインタの下位2バイトだけを書き換えると、ASLR下でも別の固定関数オフセットに飛ばせます。例:win関数が呼出元と同一page内にあれば、下位2バイトの書換だけで届くため、リーク不要でASLRを部分的に迂回できます。CTF頻出の小技。

保護別 定番回避マップ

  • Canary回避:FSBやformat string経由でcanary値を盗む→ペイロード内で同値に再現してret書換。マルチスレッドではfork後にcanaryが継承される性質を利用してbrute-force回避もある
  • NX回避:shellcode諦め→ROP / ret2libc / ret2csu / SROP(Sigreturn-Oriented Programming)
  • ASLR/PIE回避:libc関数アドレスをリーク(puts(puts@got)等)→libc_base = leak - libc.symbols['puts']
  • RELRO Full回避:GOT書換は無理→__free_hook/__malloc_hook(glibc < 2.34)、または_IO_2_1_stdout_のvtableを書く
  • すべて緑+CET+メモリ安全言語:古典攻撃ほぼ無効。ロジック脆弱性や認証バグなど、別の入り口を探す段階に移行
Canary/NX/ASLR/RELROを城の4層防御に例えて1層ずつ突破するたとえイラスト
図2:城を1層ずつ崩していく=保護機構の突破

新世代保護機構:CET・PAC・MTE・Scudo

古典4層を越えるため、現代CPUとモバイルOSは『そもそも攻撃の前提を壊す』ハードウェア保護を装備し始めています。

ここからの新世代保護は、発想がひと味違います。古典4層が『攻撃を難しくする』壁だったのに対し、CETやMTEは『そもそも攻撃の前提を成り立たせない』——たとえば“戻りアドレスは書き換えられて当たり前”という前提を、ハードウェアごとひっくり返してしまうんです。攻撃者が長年使ってきた土台そのものを崩しにかかる、いわば“ちゃぶ台返し”の防御だと言えます。

  • Intel CET-Shadow Stack:HW専用の影スタックがcall時にretアドレスを保存、ret時に通常スタックと比較。BoF/ROPで戻りアドレスを書き換えると即CPU例外。Tiger Lake(2020)以降のIntel CPUとWindows 11・Linux 6.x系で実用化
  • Intel CET-IBT(Indirect Branch Tracking):間接call/jmpの先頭にendbr64命令を要求。攻撃者ガジェットの多くはendbr64を持たないため弾かれる
  • ARM PAC(Pointer Authentication):関数ポインタの上位ビットに暗号署名を付与、改ざんを検出。Apple M1/M2やAndroid ARMv8.3+で実装
  • ARM MTE(Memory Tagging Extension):16バイト粒度でメモリにタグを付与、アクセス時にポインタのタグと比較。UAF/Heap-BoFを確率的に検出。Pixel 8+で本番採用
  • Hardened allocator:Scudo(Android標準)、GWP-ASan(確率的ASan)、mimalloc-secureでヒープ攻撃を本番環境でも低オーバーヘッドで検出
Intel CET・ARM PAC・MTE・Hardened mallocの新世代保護機構4種類の図解
図3:新世代の保護機構4種(CET/PAC/MTE/Scudo)

CTFでやってみよう:checksec読み→突破口決定

やってみよう / 自分の環境・CTFのみ

checksec出力から exploit戦略を組み立てる

目的は『checksecの4項目を見ただけで作戦が立つ』状態にすることです。CTF本番では時間が命なので、ルーチン化が効きます。

  1. checksec --file=./vuln で4項目(Canary/NX/PIE/RELRO)を確認
  2. Canary=Yes → リーク手段(%p等のFSB or fork後brute-force)を探す
  3. NX=Yes(=ほぼ常時) → shellcode諦め、ROPで構築
  4. PIE=Yes → libc関数アドレスのリーク経路を確保(puts等)
  5. RELRO=Full → GOT書換は諦め、__free_hook(glibc < 2.34)/_IO_FILE系を狙う
  6. pwntoolsでelf.symbols['main']/libc.address = leak - libc.symbols['puts']等を使って自動計算
  7. すべてONなら、Pwn以外(認証バグ・ロジック脆弱性)を探すフェーズに切り替える判断
  8. 余裕があればCETが有効な環境で過去のexploitが動かないことを確認
本番バイナリ・他者システムへの適用厳禁。CTF・自作バイナリ・許可済みのみ。CET検証は対応CPUの環境を用意。

守る側:『すべてON + 新世代を順次追加』

ビルド時推奨フラグ完全版
  • -fstack-protector-strong(Canary)、-fstack-clash-protection(大きなスタック割り当て検出)
  • -fPIE -pie(PIE/ASLR有効化)、リンクも対応必要
  • -Wl,-z,relro -Wl,-z,now(Full RELRO)、ライブラリ含む全プロジェクトで
  • -D_FORTIFY_SOURCE=2 -O2(libc強化チェック、buffer overflow検出)
  • -fcf-protection=full(CET-IBT+Shadow Stack有効化、対応コンパイラ必須)
  • -fzero-call-used-regs=used-gpr(関数呼出後にレジスタゼロクリア、ROP情報リーク削減)
  • CIでchecksecを全成果物に対して回し、退化を検出。1項目でも欠けたらビルド失敗にする
  • 本番にはScudo/GWP-ASanのHardened allocatorを採用
  • 新規プロダクトはRust/Go等のメモリ安全言語でこれら保護の必要性そのものを下げる

『城を1層ずつ崩す』ってイメージで分かりやすいね。

次回はPwn章の総まとめ。実戦シナリオ+チートシートで完走するよ。

まとめ:『1層ずつリークして崩す』

今回のポイント
  • 4大保護:Canary / NX / ASLR/PIE / RELRO、それぞれ守る範囲が違う
  • 突破はリーク→計算→上書きの繰り返し、層が多いほど手数が増える
  • 新世代:CET-Shadow Stack/IBT, ARM PAC/MTE, Scudo/GWP-ASan
  • 守りはすべてON+新世代+メモリ安全言語の組合せ、CIで退化を検出

今日の持ち帰りは『保護機構は“偵察対象”であり“設計図”でもある』。攻める人は checksec で立っている壁を見て手数を読み、守る人は全部の壁を立ててCIで退化を見張る。そして時代は、壁を高くする発想から“前提を壊す”新世代保護へ。攻防の最前線がいちばん速く動く、それがこの保護機構の領域です。

次回はPwn章の総まとめ。実戦シナリオとチートシートで全Pwn技術を統合して完走します。

次に読みたい記事

参考資料

記事URLをコピーしました