AIエージェントはファジングの人手作業をどこまで代替できるか――GitHub Security LabのFuzzing Taskflowを読む

AI

ファジングのボトルネックは、実行そのものではなく運用にある

C/C++のメモリ破壊や入力処理の不備を見つける手法として、ファジングは広く利用されています。AFL++などのファジングエンジンに大量の入力を与え、クラッシュや未到達コードを探す仕組みです。

一方で、ファジングを継続的に有効活用するには、単にツールを起動するだけでは足りません。実務では、次のような作業が発生します。

  • どの関数を対象にするか決める
  • ファジングハーネスを作成する
  • ビルドシステムや依存関係を調整する
  • カバレッジを確認して未到達箇所を特定する
  • 新しいシードやハーネスを追加する
  • クラッシュを再現し、重複を排除する
  • 脆弱性か、ハーネス側の不具合かを判定する
  • 開発者向けの報告書を作成する

GitHub Security Labが公開したオープンソースの Fuzzing Taskflow は、こうした反復作業をLLMエージェントに担わせる試みです。対象リポジトリを指定すると、エントリポイントの特定からハーネス生成、AFL++による実行、カバレッジ改善、クラッシュ分析、レポート作成までを一連のタスクフローとして実行します。

Fuzzing Taskflowの全体像

Fuzzing Taskflowは、GitHub Security Labの Taskflow Agent を基盤としています。設計上の重要なポイントは、LLMエージェントと実行ツールの役割を分けていることです。

  • LLMエージェントは、何をファジングするか、どのハーネスを作るか、どのカバレッジ不足を追うかを判断する
  • MCPツールは、AFLの実行、ハーネスのコンパイル、カバレッジ取得、クラッシュ保存などを担当する
  • 各ステージの状態は、メモリ上の受け渡しではなくSQLiteデータベースに保存される
  • シェルスクリプトが複数のタスクフローを順番に実行する

この分離により、エージェントがファジングエンジンやコンパイラを直接操作するのではなく、定義された操作単位を組み合わせて処理を進める構成になっています。AIに判断を委ねつつ、実処理のインターフェースをツール側に限定する考え方は、セキュリティ自動化全般にも応用できます。

リポジトリを指定するだけでハーネス生成まで進む

最も簡単な実行方法は、プロジェクトを起動したCodespaceなどで、次のスクリプトを実行することです。

./scripts/fuzzing/run_fuzzing.sh PROJECT

たとえば、対象リポジトリをGitHubのオーナー名とリポジトリ名で指定します。

./scripts/fuzzing/run_fuzzing.sh tukaani-project/xz

小規模な動作確認には、次のようなプロジェクトを指定できます。

./scripts/fuzzing/run_fuzzing.sh DaveGamble/cJSON

実行後、エージェントはおおむね次の準備を自動で進めます。

  • AFLなど必要なソフトウェアの導入
  • 対象リポジトリのクローン
  • ファジング対象として有望な関数の特定
  • 対象関数を呼び出すハーネスの生成
  • ハーネスのビルドとファジングの開始

これは、ファジング未導入のOSSメンテナーにとって特に有用です。従来は、ライブラリのAPIや入力形式を理解したうえで、プロジェクト固有のハーネスを手作業で準備する必要がありました。その初期コストを下げられる可能性があります。

カバレッジを読んで、次の一手を自動化する

Fuzzing Taskflowの中心的な機能は、カバレッジに基づく反復改善です。

ハーネスごとにAFLを一定時間実行した後、生成されたキューをカバレッジ用のバイナリで再生し、ソースコードの行や分岐のカバレッジを取得します。エージェントは未到達の分岐を読み取り、次のような対応を選択します。

  • 未到達分岐に到達しそうなシードを追加する
  • ハーネスに別のAPI呼び出しを追加する
  • 条件分岐で比較されている定数をAFL辞書へ追加する
  • ベンダーコードや優先度の低いエラーパスを対象外にする

ファジング時間は、30秒、60秒、120秒、240秒、480秒、960秒というように段階的に延長されます。初期段階では短時間で到達しやすいコードを効率よく探索し、難しい分岐には後半で長い時間を割く設計です。

さらに、2回連続でカバレッジの増加が設定した閾値を下回ると、改善が頭打ちになったと判断して次へ進みます。記事で示されているデフォルトの閾値は、行カバレッジの絶対値で1%です。

この仕組みの意義は、AIが単にファジングを起動するだけでなく、従来は人間が担っていた「カバレッジレポートを読む」「次の入力やハーネスを考える」という判断まで自動化しようとしている点にあります。

構造化された入力にも対応する仕組み

AFLの標準的なバイト列変異は、単純なバイナリ入力には強い一方、JSONやXMLのように構文構造を持つ入力では効率が下がることがあります。Fuzzing Taskflowは、構造を意識した入力生成のために複数の方法を組み合わせています。

  • JSON、XML、正規表現、PNG、長さ付きバイナリTLV向けの辞書やカスタムミューテーター
  • 対象ソースから文字列リテラルや数値定数を抽出するソースレベル辞書
  • 未到達分岐付近のmemcmp、strncmp、caseなどから比較対象を抽出する動的辞書
  • 既存コーパスの一部を組み合わせるコーパス・スプライス操作

特に興味深いのは、カバレッジ改善の過程で辞書が成長する点です。対象コードが入力中で比較しているマジック値を抽出し、次の反復で利用します。プロトコルやファイル形式に関する重要な定数がソースコードに書かれている場合、入力生成の手がかりとして活用できます。

また、各ハーネスのコーパスは反復をまたいで維持され、afl-cminで整理されます。キャンペーンを再開しても、過去に見つけた有効な入力を再利用できるため、毎回ゼロから探索し直すことを避けられます。

ファジング用とカバレッジ計測用でバイナリを分ける理由

Fuzzing Taskflowでは、各ハーネスを2種類のバイナリとしてビルドします。

  • .aflバイナリ:AFLのエッジ計測と、AddressSanitizerおよびUndefinedBehaviorSanitizerを利用してファジングする
  • .covバイナリ:LLVMのプロファイル計測とカバレッジマッピングを利用して、ソース行や分岐のカバレッジを取得する

AFLの計測情報はファジングエンジンにとって重要ですが、人間が読めるソースレベルのカバレッジレポートとは目的が異なります。そこで、ファジングに適した実行形式と、分析に適した実行形式を分けています。

これは独自のファジング基盤を作る場合にも参考になる設計です。ファジングの進捗をバイト列やエッジ数だけで評価するのではなく、開発者が確認しやすい行・分岐カバレッジへ変換することで、次の改善策を立てやすくなります。

クラッシュの重複排除から報告書作成まで

クラッシュを見つけることは、脆弱性対応の始まりにすぎません。同じ原因から発生したクラッシュをまとめ、再現性や影響を調査しなければ、開発チームは対応の優先順位を決められません。

Fuzzing Taskflowでは、ファジング後に次の処理を自動化します。

  1. afl-tminでクラッシュ入力を最小化する
  2. ASanで再実行し、スタックトレースを取得する
  3. 正規化したスタック上部のハッシュを使って重複を排除する
  4. 過去に見つかったクラッシュを現行バイナリで再生し、修正済みか確認する
  5. ハーネスと対象関数の呼び出し経路を解析し、Markdown形式の報告書を生成する

報告書では、次のような判定結果が使われます。

  • vulnerability
  • library_hardening
  • harness_bug
  • OOM
  • timeout
  • assertion_failure
  • duplicate

さらに、根本原因、ファイルと行番号、公開APIからの到達可能性、悪用可能性、修正案のUnified Diff、回帰テストの案も含まれます。

ここで重要なのは、AIが作った脆弱性判定や修正案を最終結論として扱わないことです。ライブラリ内部の前提条件、呼び出し元の制約、実際の製品構成までLLMが正確に把握できるとは限りません。報告書は、人間のレビューを始めるための質の高い材料と位置付けるべきです。

OSS-Fuzzや手動ファジングとの違い

OSS-Fuzzのような継続的ファジング基盤は、長期的な実行、クラッシュ検知、プロジェクトへの統合に強みがあります。一方、継続運用では、カバレッジの監視、ハーネスの追加、クラッシュのトリアージといった人手が依然として必要です。

Fuzzing Taskflowは、既存の継続的ファジング基盤を置き換えるというより、次の領域を自動化するアプローチと考えられます。

観点 従来の手動作業 Fuzzing Taskflowのアプローチ
対象選定 開発者や研究者がコードを調査 エージェントが候補関数を分析
ハーネス作成 API仕様を調べて実装 エージェントが生成・ビルド
カバレッジ改善 レポートを人間が確認 未到達分岐から次の行動を選択
入力生成 辞書やシードを手作業で追加 ソースやガードから辞書を拡張
クラッシュ対応 再現、最小化、重複排除を実施 これらを自動化し報告書を生成
最終判断 セキュリティ担当者が実施 AIの分析を人間がレビュー

新規プロジェクトの初期調査や、既存のハーネスでは到達できていないコードの探索に適しています。反対に、製品リリースの最終判定や脆弱性の確定を無人で行うものではありません。

実行時の最大の注意点は、AIではなく権限設計

Fuzzing Taskflowを試す際に最も注意すべきなのは、実行環境です。紹介されている構成では、コンテナを挟まずにホスト上でafl-fuzz、clang、およびLLMが選択した任意のビルドコマンドを実行します。

対象リポジトリの内容やビルド設定に悪意ある指示が含まれていた場合、プロンプトインジェクションによってエージェントが意図しない操作を実行する可能性があります。したがって、少なくとも次の原則を守る必要があります。

  • Codespaceや使い捨てVMなど、破棄可能な環境で実行する
  • ホストOSや重要な開発環境で直接実行しない
  • 管理者権限や特権を与えない
  • 秘密情報、クラウド認証情報、SSH鍵を環境へ持ち込まない
  • ネットワークアクセスやファイル共有を必要最小限にする
  • 実行後に生成物、プロセス、設定変更を確認する

AIエージェントを開発ツールとして導入する場合、モデルの精度だけでなく、エージェントが触れられるファイル、コマンド、ネットワーク、認証情報を制限することが重要です。これはファジングに限らず、AIによるCI/CD操作や自動修正にも共通する課題です。

エンジニアが導入前に評価すべきポイント

実際に導入を検討する場合は、検出数だけでなく、運用に組み込めるかを評価する必要があります。

  • 生成されたハーネスが対象ライブラリの公開APIを適切に呼び出しているか
  • カバレッジが既存のハーネスやOSS-Fuzz環境と比べて改善しているか
  • クラッシュの重複排除が、異なる根本原因を誤ってまとめていないか
  • harness_bugと実際のライブラリ不具合を正しく区別できているか
  • 生成された修正案に回帰テストが伴っているか
  • 長時間実行時のCPU、メモリ、ストレージ消費が許容範囲か
  • レポートをIssue管理や脆弱性対応フローへ接続できるか
  • エージェントが実行するコマンドとアクセス権を監査できるか

特にC/C++プロジェクトでは、ビルドシステム、条件コンパイル、外部依存、グローバル状態などがハーネス生成の成否を左右します。ハーネスがコンパイルできたとしても、現実の利用経路を十分に再現しているとは限りません。カバレッジの数字だけで品質を判断しないことが大切です。

AIはファジング担当者を置き換えるのではなく、探索範囲を広げる

Fuzzing Taskflowが示しているのは、AIがファジングエンジンそのものを発明したということではありません。AFL++、サニタイザー、カバレッジ計測、クラッシュ最小化といった既存技術を、LLMエージェントが判断ループとして接続している点に価値があります。

これまで専門家が行っていた、ハーネス作成、カバレッジ確認、入力改善、クラッシュの一次分析を自動化できれば、限られたセキュリティ人材でもより多くのコードを継続的に調査できます。一方で、脆弱性の確定、影響範囲の判断、修正の妥当性確認は、依然として人間の責任です。

実務での現実的な使い方は、AIに最終判断を渡すことではなく、探索と一次整理を自動化し、専門家が重要な判断に集中できるようにすることでしょう。C/C++のOSSメンテナーやセキュリティチームにとって、Fuzzing Taskflowはその可能性と、AIエージェントを安全に動かすための条件を同時に考える材料になります。

GitHub Security LabのFuzzing Taskflow

GitHub Security LabのFuzzing 101

出典: GitHub Blog

タイトルとURLをコピーしました