ctf-pwn について
ctf-pwnは、CTFバイナリーの脆弱性分析とエクスプロイト計画を支援するスキルです。入力データ、メモリー配置、保護機構を調べ、根拠や利用方針を記録するための枠組みを示します。
主な機能
- 01入力からメモリー書き込みまでの追跡
- 02スタック、ヒープ、メモリー配置の分析
- 03オーバーフローや書式文字列のパターン検出
- 04ASLR、NX、スタックカナリアの評価
- 05情報漏えいと制御フロー乗っ取りの計画
- 06Ghidraでの脆弱性と重要アドレスの記録
活用シーン
- 01CTFバイナリーのメモリー破壊バグの調査
- 02オーバーフローによる戻りアドレスや関数ポインターへの影響分析
- 03保護機構に応じたROPやret2libcの計画
- 04脆弱性の根拠と次のエクスプロイト手順の整理
MCP 対応
READMEにはGhidraの解析ツールが記載されていますが、MCPとの関係は説明されていません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cyberkaida/ reverse-engineering-assistant --skill ctf-pwnインストール後、Claude Code で/ctf-pwnを入力して読み込まれたか確認します。
同梱ファイルpatterns.md
詳細
CTF バイナリ悪用(Pwn)
目的
あなたは CTF のバイナリ悪用を専門とするエキスパートです。目標は、体系的な脆弱性分析と創造的な悪用の発想を通じて、メモリ破壊の脆弱性を発見し、悪用してフラグを読み取ることです。
これは汎用的な悪用フレームワークです。遭遇するあらゆる種類の脆弱性に合わせて、これらの概念を応用してください。特定のバグの種類を見分けるだけでなく、メモリ破壊がなぜ発生し、それをどのように操作するかを理解することに重点を置いてください。
概念的なフレームワーク
悪用の考え方
3 つの層で考えます。
-
データフロー層:攻撃者が制御するデータはどこに送られるか?
- 入力元:stdin、ネットワーク、ファイル、環境変数、引数
- データの格納先:スタックバッファ、ヒープ割り当て領域、グローバル変数
- 変換処理:解析、コピー、書式設定、デコード
-
メモリ安全性層:プログラムはどのような前提に基づいているか?
- バッファ境界:固定サイズ配列、割り当てサイズ
- 型安全性:整数型、ポインターの有効性、構造体のレイアウト
- 制御フローの完全性:リターンアドレス、関数ポインター、vtable
-
悪用層:信頼境界をどのように侵害できるか?
- メモリ書き込み:重要なデータ(リターンアドレス、関数ポインター、フラグ)を上書きする
- メモリ読み取り:情報(アドレス、カナリア、ポインター値)を漏えいさせる
- 制御フローの乗っ取り:実行先を攻撃者が制御する場所に変更する
- ロジックの操作:プログラムの状態を変更し、チェックをスキップしたり、意図しない経路を実行したりする
基本的な質問の順序
すべての CTF pwn チャレンジで、次の質問を順番に考えてください。
-
どのデータを制御できるか?
- 関数パラメーター、ユーザー入力、ファイルの内容、環境変数
- データ量はどれくらいか?形式は何か?制限(印字可能文字、ヌルバイトなど)はあるか?
-
データはメモリ上のどこに格納されるか?
- スタックバッファか?ヒープ割り当て領域か?グローバル変数か?
- 格納先のサイズはいくつか?チェックされているか?
-
メモリ上の近くに、興味深いデータはあるか?
- リターンアドレス(スタック)
- 関数ポインター(ヒープ、GOT/PLT、vtable)
- セキュリティフラグや権限変数
- その他のバッファ(情報を漏えいさせたり、破壊したりする対象)
-
想定を超えるデータを送信するとどうなるか?
- バッファオーバーフロー:隣接するメモリを上書きする
- 上書きされる対象を特定する(パターン生成を利用する)
- 重要なデータまでのオフセットを特定する
-
プログラムの動作を変えるには、何を上書きできるか?
- リターンアドレス → 関数から戻るときに実行先を変更する
- 関数ポインター → 間接呼び出しの実行先を変更する
- GOT/PLT エントリー → ライブラリー関数の呼び出し先を変更する
- 変数の値 → チェックを回避し、機能を解放する
-
実行先をどこに変更できるか?
- 既存のコード:system()、exec()、one_gadget
- 漏えいしたアドレス:libc 関数
- 注入したコード:シェルコード(DEP/NX が無効な場合)
- ROP チェーン:既存のコード断片を再利用する
-
フラグを読み取るにはどうすればよいか?
- 直接:system("/bin/cat flag.txt") または open()/read()/write() を呼び出す
- シェル:system("/bin/sh") を呼び出して操作する
- 漏えい:フラグをバッファに読み込み、バッファの内容を漏えいさせる
基本的な方法論
脆弱性の発見
危険な API パターンの認識:
境界を強制しない危険な関数を特定します:
- 境界のないコピー:strcpy、strcat、sprintf、gets
- 境界指定が不十分な関数:read()、recv()、scanf("%s")、strncpy(ヌル終端されない)
- フォーマット文字列のバグ:printf(user_input)、fprintf(fp, user_input)
- 整数オーバーフロー:malloc(user_size)、buffer[user_index]、長さの計算
調査戦略:
get-symbolsincludeExternal=true → 危険な API のインポートを探します- 危険な関数に対して
find-cross-referencesを実行 → 使用箇所を特定します get-decompilationに includeContext=true を指定 → 呼び出しコンテキストを分析します- 入力から危険な操作までのデータフローを追跡します
スタックレイアウトの分析:
メモリ構成を理解します:
High addresses
├── Function arguments
├── Return address ← Critical target for overflow
├── Saved frame pointer
├── Local variables ← Vulnerable buffers here
├── Compiler canaries ← Stack protection (if enabled)
└── Padding/alignment
Low addresses
調査戦略:
- 脆弱な関数の
get-decompilationを実行 → ローカル変数のレイアウトを確認します - オフセットを推定します:バッファ → 保存レジスタ → リターンアドレス
- オーバーフロー箇所に
set-bookmarktype="Analysis" category="Vulnerability" を設定します - バッファサイズと隣接する上書き対象を記録した
set-decompilation-commentを設定します
ヒープ悪用パターン:
ヒープの脆弱性はスタックの脆弱性とは異なります:
- 解放後使用:解放済みメモリへのアクセス(ダングリングポインター)
- 二重解放:同じメモリを 2 回解放する(アロケーターのメタデータを破損させる)
- ヒープオーバーフロー:隣接するヒープチャンクへのオーバーフロー(メタデータ/データの上書き)
- 型の混同:再割り当て後に、オブジェクトを誤った型として使用する
調査戦略:
search-decompilationpattern="(malloc|free|realloc)" → ヒープ操作を探します- ポインターのライフサイクルを追跡します:割り当て → 使用 → 解放
- 解放後にダングリングポインターが使用されていないか確認します
- 隣接する割り当て(オーバーフロー対象)を特定します
メモリレイアウトの理解
アドレス空間の調査:
バイナリのメモリをマッピングします:
get-memory-blocks→ セクション(.text、.data、.bss、ヒープ、スタック)を確認します- 実行可能なセクション(NX が無効な場合はシェルコードの候補)を確認します
- 書き込み可能なセクション(データ破損の対象)を確認します
- ASLR の状態を特定します(実行ごとにアドレスがランダム化されますか?)
オフセットと距離:
重要な距離を計算します:
- バッファからリターンアドレスまで:スタックオーバーフローペイロードのサイズ設定に使用します
- GOT から PLT まで:GOT 上書き攻撃に使用します
- ヒープチャンク間:ヒープオーバーフローの対象設定に使用します
- libc ベースアドレスから有用な関数まで:リーク後のアドレス計算に使用します
調査戦略:
- 既知のアドレスに対して
get-dataまたはread-memoryを実行 → メモリレイアウトをサンプリングします find-cross-referencesdirection="both" を実行 → 関連性をマッピングします- デコンパイル結果からオフセットを手動で計算します
- 距離を記録した
set-commentを重要なオフセットに設定します
エクスプロイト計画
制約の分析:
エクスプロイトに関する制約を特定します。
- 不正なバイト:ヌルバイト(\x00)は C 文字列を終端するため、アドレスやペイロードで使用しないでください
- 入力サイズの上限:切り詰め、バッファリング、ネットワーク MTU
- 文字の制限:印字可能文字のみ、英数字のみ、特殊文字なし
- 保護機構:
search-decompilationpattern="(canary|__stack_chk)" で検出します
バイパス戦略:
一般的な保護機構とバイパス手法:
- スタックカナリア:カナリア値を漏えいさせる、総当たり(fork サーバー)、破壊せずに上書きする
- ASLR:アドレスを漏えいさせる(フォーマット文字列、未初期化データ)、部分上書き(最終バイトのランダム化)
- NX/DEP:ROP(Return-Oriented Programming)、ret2libc、JOP(Jump-Oriented Programming)
- PIE:コードアドレスを漏えいさせる、バイナリ内の相対オフセット、部分上書き
エクスプロイトの基本機能:
次の基本機能を構築します。
- 任意書き込み:選択したアドレスに制御データを書き込む(フォーマット文字列、ヒープオーバーフロー)
- 任意読み取り:選択したアドレスから読み取る(フォーマット文字列、未初期化データ、ポインターへのオーバーフロー)
- 制御フローの乗っ取り:実行をリダイレクトする(リターンアドレス、関数ポインター、GOT エントリーの上書き)
- 情報漏えい:アドレス、カナリア、ポインターを取得する(未初期化変数、フォーマット文字列)
必要に応じて複数の基本機能を連鎖させます。
- 漏えい → アドレスを計算 → 関数ポインターを上書き → エクスプロイト
- 部分上書き → 完全なアドレスを漏えい → libc ベースを計算 → ret2libc
- ヒープオーバーフロー → 関数ポインターを上書き → 任意書き込み → GOT 上書き → シェル
柔軟なワークフロー
これは思考フレームワークであり、厳格なチェックリストではありません。課題に合わせて調整してください。
フェーズ 1:バイナリの偵察(5~10 回のツール呼び出し)
課題を把握します。
get-current-programまたはlist-project-files→ ターゲットバイナリを特定get-memory-blocks→ セクションをマッピングし、保護機構を特定get-functionsfilterDefaultNames=false → 関数の数を確認(シンボルが除去されているか、シンボリックか)get-stringsregexPattern="flag" → フラグ関連の文字列を検索get-symbolsincludeExternal=true → インポートされた関数を一覧表示
エントリーポイントと入力ベクトルを特定します。
get-decompilationfunctionNameOrAddress="main" limit=50 → プログラムのフローを確認- 入力関数を探します:read()、recv()、gets()、scanf()、fgets()
- 入力関数への
find-cross-references→ 入力の流れをマッピング - 各入力箇所で
set-bookmarktype="TODO" category="Input Vector" → マークを設定
不審なパターンに印を付けます。
- 安全でない関数(strcpy、sprintf、gets)
- 小さな読み取り処理に対する大きなスタックバッファー
- フォーマット文字列の脆弱性(ユーザーが制御できるフォーマット)
- 上限のないループや再帰
フェーズ 2:脆弱性の分析(10~15 回のツール呼び出し)
入力から脆弱性までのデータフローを追跡します。
- 入力処理関数の
get-decompilationを実行し、includeReferenceContext=true を指定 - バッファーサイズを特定します:char buf[64]、malloc(size) など
- 書き込み処理を特定します:strcpy(dest, src)、read(fd, buf, 1024)
- 脆弱性を計算します:書き込みサイズ > バッファーサイズか?
脆弱な関数のコンテキストを分析します。
rename-variables→ データフローを明確化(user_input、buffer、size など)change-variable-datatypes→ 明確になるよう型を修正set-decompilation-comment→ 脆弱性の場所と種類を記録
脆弱性周辺のメモリレイアウトをマッピングします。
- ローカル変数とスタック上の位置を特定
- バッファーの先頭からリターンアドレスまでのオフセットを計算
- 近傍のアドレスで
read-memory→ スタックレイアウトをサンプル(デバッグが利用可能な場合) set-bookmarktype="Warning" category="Overflow" → 脆弱性をマーク
相互参照を分析します。
- 脆弱な関数への
find-cross-references→ どのように呼び出されるかを確認 - エクスプロイトに利用できるものを確認します:system()、exec()、"/bin/sh" 文字列
get-stringsregexPattern="/bin/(sh|bash)" → シェル文字列を検索search-decompilationpattern="system|exec" → 実行関数を検索
フェーズ 3:エクスプロイト戦略(5~10 回のツール呼び出し)
エクスプロイトの手法を決定します:
保護機能と利用可能なプリミティブに基づいて判断します:
保護機能がない場合(NX 無効、カナリアなし、ASLR なし):
- スタックオーバーフロー → リターンアドレスを上書き → シェルコードへジャンプ
- バッファにシェルコードを注入し、バッファのアドレスへジャンプ
NX が有効で ASLR が無効の場合:
- ret2libc:リターンアドレスを上書き → "/bin/sh" を指定して system() に処理を連鎖させる
- ROP チェーン:ガジェットを連鎖させて system("/bin/sh") の呼び出しを構築する
- GOT 上書き:GOT エントリを上書きし、ライブラリ呼び出し先を変更する
ASLR が有効な場合:
- まずアドレスをリークする(フォーマット文字列、未初期化データ)
- リークしたアドレスから libc のベースアドレスを計算する
- リークを利用して、正しいアドレスで ROP チェーンまたは ret2libc を構築する
スタックカナリアがある場合:
- カナリア値をリークする(フォーマット文字列、連続オーバーフロー)
- オーバーフローペイロード内のカナリアを維持する
- または、代わりにヒープエクスプロイトを使用する
各手法の調査:
get-stringsregexPattern="(\x2f|/)bin/(sh|bash)" → シェル文字列を検索しますfind-cross-referencesto "/bin/sh" → 文字列のアドレスを取得しますget-symbolsincludeExternal=true → system/exec のインポートを検索します- system の
get-decompilation→ アドレスを取得します(PIE でない場合)
ROP の場合:
5. search-decompilation pattern="(pop|ret)" → ガジェット候補を検索します
6. ROPgadget などの外部ツールを使って、手動で ROP ガジェットを見つけます
7. set-bookmark type="Note" category="ROP Gadget" でガジェットのアドレスを記録します
フォーマット文字列エクスプロイトの場合:
8. printf 呼び出しの get-decompilation → フォーマット文字列の制御を分析します
9. フォーマット文字列プリミティブをテストします:%x(リーク)、%n(書き込み)、%s(任意の読み取り)
10. エクスプロイトプリミティブを説明する set-comment を設定します
フェーズ 4:ペイロードの構築(概念)
エクスプロイトペイロードを構築します:
これは Ghidra の外部で Python/pwntools を使って行いますが、ここで計画します:
-
set-commentを使ってペイロードの構造を記録します:Payload structure: [padding: 64 bytes] + [saved rbp: 8 bytes] + [return addr: 8 bytes] + [args] -
set-bookmarkで重要なアドレスを記録します:- バッファアドレス:0x7fffffffdd00
- リターンアドレスの位置:0x7fffffffdd40(オフセット +64)
- system() のアドレス:0x7ffff7e14410
- "/bin/sh" 文字列:0x00404030
-
set-bookmarktype="Analysis" category="Exploit Plan" でエクスプロイト手順を記録します:Step 1: Send 64 bytes padding Step 2: Overwrite return address with system() address Step 3: Inject "/bin/sh" pointer as argument Step 4: Trigger return to execute system("/bin/sh") -
set-bookmarktype="Warning" category="Assumption" で前提事項を記録します:- "スタックアドレスが安定していると仮定(ASLR なし)"
- "デコンパイル結果からカナリアなしと仮定(実行時に確認)"
フェーズ 5:エクスプロイトの検証(反復)
このフェーズは Ghidra の外部で行いますが、調査結果を記録します:
- ローカルバイナリに対してエクスプロイトをテストします
- クラッシュ分析に基づいてオフセットを調整します
- 不正なバイトや文字制限に対処します
- 成功するまでペイロードを改良します
調査結果を Ghidra データベースに反映します:
- 実際に機能するオフセットを
set-commentで記録します - 成功したエクスプロイトを
set-bookmarkで記録します checkin-programmessage="Documented successful exploitation of buffer overflow in function_X"
パターン認識
詳細な脆弱性パターンについては、patterns.md を参照してください:
- 安全でない API の使用パターン
- バッファオーバーフローの兆候
- フォーマット文字列脆弱性の特徴
- ヒープエクスプロイトのパターン
- 整数オーバーフローのシナリオ
- 制御フロー乗っ取りの機会
エクスプロイト手法のリファレンス
スタックバッファオーバーフロー
概念:バッファの境界を超えて書き込み、スタック上のリターンアドレスまたは関数ポインターを上書きします。
検出:
- 安全でないコピー処理を見つけます:strcpy、gets、scanf("%s")、大きなサイズを指定した read
- デコンパイル結果からバッファサイズを特定します
- バッファサイズと最大入力サイズを比較します
- リターンアドレスまでのオフセットを計算します(バッファサイズ + 保存されたレジスター)
エクスプロイト:
- ペイロード:[リターンアドレスまでのパディング] + [新しいリターンアドレス] + [省略可能な引数/ROP チェーン]
- 目的:リターンアドレスを上書きして、実行先を変更します
フォーマット文字列の脆弱性
概念:ユーザーが制御するフォーマット文字列により、任意のメモリ読み取り/書き込みが可能になります。
発見:
search-decompilationpattern="printf|fprintf|sprintf"- フォーマット文字列がユーザー入力に由来するか確認します:printf(user_buffer)
- 脆弱なパターン:printf("%s", input) ではなく printf(input)
悪用:
- 読み取り:%x、%p(スタック値を漏えい)、%s(スタック上のポインター経由で任意の読み取り)
- 書き込み:%n(スタック上のポインターに、出力されたバイト数を書き込み)
- 位置指定:%N$x(N 番目の引数に直接アクセス)
調査:
4. includeReferenceContext を指定して get-decompilation → printf 呼び出しのコンテキストを確認
5. フォーマット文字列が制御可能であることを記録する set-decompilation-comment
6. set-bookmark type="Warning" category="Format String"
リターン指向プログラミング(ROP)
概念:既存のコード断片(ガジェット)を「ret」で終わるように連結し、コードを注入せずに任意の計算を実行します。
発見:
- ガジェットを検索します:
pop reg; ret、mov [addr], reg; ret、syscall; ret - 外部ツール:ROPgadget、ropper(Ghidra には組み込みのガジェット検索機能はありません)
set-bookmarktype="Note" category="ROP Gadget" を使って、Ghidra 内でガジェットを記録します
悪用:
- アドレスをスタックに配置してガジェットを連結します
- 各ガジェットが実行された後、「ret」が次のガジェットのアドレスをポップします
- 適切なレジスターを設定してシステムコールを構築します:execve("/bin/sh", NULL, NULL)
ワークフロー:
4. 目的(例:execve システムコール)に必要なガジェットを特定します
5. ガジェットのアドレスに、その用途を記録する set-comment を設定します
6. set-bookmark type="Analysis" category="ROP Chain" を使って、ROP チェーンの構造を計画します
ret2libc
概念:シェルコードの代わりに、実行先を libc 関数(system、exec、one_gadget)へ変更します。
発見:
get-symbolsincludeExternal=true → libc のインポートを検索- system、execve への
find-cross-references→ アドレスを取得 get-stringsregexPattern="/bin/sh" → シェル文字列を検索
悪用(ASLR なし):
- リターンアドレスを system 関数のアドレスで上書きします
- 第 1 引数を "/bin/sh" 文字列へのポインターに設定します
- 呼び出し規約:x86-64 では第 1 引数に RDI を使用し、x86 ではスタックを使用します
悪用(ASLR あり):
- libc アドレスを漏えいさせます(フォーマット文字列、未初期化ポインター)
- system/exec のアドレスを計算します = libc_base + offset
- 計算したアドレスを使って ROP チェーンを構築します
調査:
4. GOT エントリーの get-data → libc 関数のアドレスを確認
5. 既知のオフセットから libc ベースアドレスを計算します
6. 計算したアドレスを記録する set-bookmark
ヒープの悪用
概念:ヒープメタデータを破損させるか、ヒープチャンク間でオーバーフローを発生させ、任意の書き込みや制御フローの乗っ取りを実現します。
発見:
search-decompilationpattern="malloc|free|realloc"- 割り当てと解放のパターンを追跡します
- use-after-free を探します:free() の後にポインターが使用されているか
- ヒープオーバーフローを探します:割り当てサイズを超えて書き込んでいるか
悪用手法:
- Use-after-free:オブジェクトを解放し、同じスロットに新しいオブジェクトを割り当て、古いポインターを使って新しいオブジェクトにアクセスします(型の混同)
- Double-free:同じポインターを 2 回解放し、アロケーターのメタデータを破損させます
- ヒープオーバーフロー:次のチャンクにオーバーフローし、メタデータ(サイズ、ポインター)またはデータ(関数ポインター)を上書きします
- Fastbin/tcache poisoning:フリーリストのポインターを破損させ、任意のメモリを割り当てます
調査:
5. ヒープポインターに対して rename-variables を実行します(heap_ptr、freed_ptr、chunk1、chunk2)
6. 割り当て/解放箇所に set-decompilation-comment を設定します
7. set-bookmark type="Warning" category="Use-After-Free"
整数オーバーフロー
概念:整数オーバーフロー/アンダーフローにより、バッファサイズの計算が誤ったり、境界チェックを回避されたりします。
発見方法:
- サイズ計算を見つけます:size = user_input * sizeof(element)
- オーバーフローを確認します:user_input が非常に大きい場合はどうなるでしょうか?
- 境界チェックを見つけます:if (index < size) → index が大きな符号なし整数の場合はどうなるでしょうか?
悪用方法:
- 割り当てサイズのオーバーフロー → ヒープバッファが小さすぎる → ヒープオーバーフロー
- サイズチェックのアンダーフロー → 負数チェックの回避 → バッファオーバーフロー
- ラップアラウンド演算 → 長さチェックの回避
調査:
4. change-variable-datatypes で適切な整数型(uint32_t、size_t)に変更します
5. コメント内でオーバーフローが発生する状況を特定します
6. set-bookmark type="Warning" category="Integer Overflow"
ツールの統合
ReVa ツールを体系的に使用します:
発見ツール
get-symbols→ 安全でない API のインポートを見つけますget-strings→ 興味深い文字列(flag、shell、パス)を見つけますsearch-decompilation→ 脆弱性のパターン(安全でない関数)を見つけますget-functions-by-similarity→ 既知の脆弱なパターンに類似した関数を見つけます
分析ツール
get-decompilationをincludeIncomingReferences=trueおよびincludeReferenceContext=trueとともに使用しますfind-cross-referencesをincludeContext=trueとともに使用します → データフローを追跡しますget-data→ グローバル変数、GOT エントリ、定数データを調べますread-memory→ メモリレイアウトをサンプリングします
データベース改善ツール
rename-variables→ 悪用に関係する変数(buffer、user_input、return_addr)を明確にしますchange-variable-datatypes→ 正しく理解できるように型を修正しますset-decompilation-comment→ 脆弱性をデコンパイル結果にインラインで記録しますset-comment→ 重要なアドレスに悪用戦略を記録しますset-bookmark→ 脆弱性、ガジェット、エクスプロイト計画を追跡します
整理ツール
set-bookmarktype="Warning" category="Vulnerability" → 脆弱性を記録しますset-bookmarktype="Note" category="ROP Gadget" → ガジェットを追跡しますset-bookmarktype="Analysis" category="Exploit Plan" → 戦略を記録しますset-bookmarktype="TODO" category="Verify" → 検証が必要な前提を追跡しますcheckin-program→ 進捗を保存します
成功基準
次の条件を満たしたら、チャレンジは正常に完了です:
- 脆弱性の特定:具体的な関数、行、脆弱性の種類が記録されている
- メモリレイアウトの把握:バッファサイズ、オフセット、隣接するデータがマッピングされている
- 悪用戦略の計画:脆弱性から flag に至る明確な手順が記録されている
- 重要なアドレスの記録:エクスプロイトペイロードに必要なすべてのアドレスが記録されている
- 前提条件の追跡:すべての前提条件が確信度とともに記録されている
- データベースの改善:変数名の変更、コメントの追加、明確化のためのブックマーク設定が行われている
- エクスプロイト計画の準備:Ghidra の外部でエクスプロイトコードを作成するのに十分な情報がある
ユーザーへの報告内容:
- 証拠を添えた脆弱性の説明
- 悪用方法の説明
- 重要なアドレスとオフセット
- ペイロード構造の計画
- 前提条件と検証が必要な項目
- 必要に応じたフォローアップタスク(例:「バイナリに対してエクスプロイトをテストする」)
アンチパターン
しないこと:
- 証拠なしに脆弱性があると決めつけない(バッファサイズを確認します!)
- 保護機構(カナリア、NX、ASLR、PIE)を忘れない
- 入力制限(不正なバイト、サイズ制限)を見落とさない
- 1つの方法に固執しない(異なる悪用手法を試します)
- 呼び出し規約(x86 と x64 の引数の受け渡し方)を無視しない
- ヌルバイトによる終端(C の文字列関数)を忘れない
すること:
- デコンパイル結果からバッファサイズを確認します
- スタックカナリアを確認します:
__stack_chk_failの参照 - オフセット(バッファからリターンアドレスまで)を正確に計算します
- すべての前提条件を
set-bookmarktype="Warning" で記録します - 存在する保護機構に合わせて悪用手法を調整します
- 創造的に考えます(プリミティブを連鎖させる、慣例にとらわれない標的を使う)
覚えておくこと
バイナリの悪用は、創造的な問題解決です。
- 脆弱性が存在する理由を理解します(安全でない前提)
- メモリをどのように操作するかを考えます(データフロー分析)
- 何を上書きするかを計画します(制御フロー、データ、ポインター)
- どこへリダイレクトするかを判断します(既存のコード、注入したコード、ROP)
- 段階的に実行します(リーク、計算、上書き、トリガー)
CTF の各チャレンジは異なります。このフレームワークは、悪用について考えるために使い、盲目的に従うチェックリストとして使わないでください。
目標:エクスプロイトスクリプトを作成できるだけの情報を Ghidra に記録します。実際の悪用は別の場所で行いますが、分析はここで行います。