ai-ml

キルチェーンオーケストレーションのための ReAct プロンプティング

初めて単一のモデルにペンテストキャンペーン全体をオーケストレーションさせようとしたとき、40分目で失敗した。技術的能力の欠如ではない。モデルは、公開された Redis に対して何をすべきか、認証されていない Jenkins に対して何をすべきか、SSRF をクラウドメタデータと連鎖させる方法を完全に理解していた。問題は別のもので、もっと退屈なものだった:40ページのコンテキストで、エージェントは最初に与えたスコープを忘れていた。スコープ外の範囲をスキャンし始め、私たちのツールボックスには存在しない2つのツール(nmap-fast-aggressiveburp-pro-headless、どちらも実在しない)を幻覚し、クライアントがインストールすらしていなかった CVE を悪用することを提案した。

その夜、オーケストレーターの大部分を書き直した。そこから生まれたのが、今日の Gandalf CLI のエンジンの基礎である:複数のモデルが同じ屋根の下で協力する長期間、複数フェーズのキャンペーンに適合させた、ReAct フレームワークのかなり頑固な拡張。この記事は、なぜそのデザインに到達したのか、何を借りたのか、何を自分たちで発明しなければならなかったのか、そしてどこで顔を打ち続けているのかを語る試みである。

マーケティング抜きの ReAct

ReAct は 2022 年末に Yao 氏らによって発表された(Yao et al., 2022)[1]。アイデアは欺くほどシンプルだ。モデルに答えだけ、またはアクションだけを求めるのではなく、3つのことを交互に行うように求める:Thought(何をすべきかについての自然言語による明示的な推論)、Action(通常は型付けされた外部ツールの呼び出し)、そして Observation(ツールが返す出力で、コンテキストに戻る)。ループ。もう一周。モデルが十分だと判断するまでそうやって続く。

重要なのは、推論がロールアウト自体にテキストとして現れることだ。これは2つのことを同時に解決する。一方では、モデルは Chain-of-Thought の能力を得る(Wei et al., 2022)[2]、つまり行動する前に問題を分解することだ。他方では、自分の頭の中に閉じこもらない:数回の反復ごとに現実世界に触れて、実際の観察を持ち帰らなければならない。これは純粋な CoT に比べて幻覚を大幅に減らす。

HotpotQA の質問や WebShop のレシピには完璧だ。ペンテストを実行するには、そうではない。

キャンペーンが1時間以上続くとき、ReAct はどこで壊れるか

実際のターゲットでバニラ ReAct で行ったほぼすべてのテストで経験した3つの失敗。

1つ目はコンパクションだ。まともなリコンセッションでの Thought/Action/Observation トレースは、見た目よりもはるかに多くのスペースを占める。妥当な /24 に対する単一の nmap -sV -p- は、Observation で数千トークンになる。20ホストで掛け算し、HTTP フィンガープリンティングと混ぜると、2時間後にはコンテキストが飽和する。オーケストレーターが新しいものに合わせて古いメッセージを切り詰め始めると、最初に失われるのは通常まさに最も重要なものである:エンゲージメントのルール、初期仮説、クライアントの制約。

2つ目は drift だ。各ラウンドで明示的に目標を再固定しないと、モデルは脇道に逸れる。「最初のベクトルを見つける」から始まり、スコープの一部ではないプロバイダのサブドメインを列挙することになる。PentestGPT(Deng et al., 2023)[3]はこの問題をかなりよく文書化し、モジュールに分割することを提案した。私たちは独自に同様の結論に達した。

3つ目はツール幻覚だ。モデルが多くの反復を経ると、ツールボックスにないアクションを発明し始める。masscan-with-bannersburp-active-scannuclei-cloud-edition。もっともらしいが存在しない名前。あなたのランナーが厳格であれば、ハードに失敗する。緩いと、もっと悪い:似たようなことをするが、あなたが頼んだものではない。

私たちが追加したもの:フェーズ別ハンドオフと型付きアーティファクト

私たちにとっての重要な直感は、キルチェーンを単一のループではなく、ステートマシンとして扱うことだった。各フェーズ(Recon、Planning、Execution、Reporting)には独自のエージェント、独自のプロンプト、独自のモデルがある。そしてフェーズ間で生のコンテキストは渡されない:artifacts が渡される。

artifact とは、あるエージェントがフェーズを閉じるときに生成し、別のエージェントが自分のフェーズを開くときに消費する、型付けされた構造のことだ。完全なトレースではない。中間の思考でもない。進行する前に2回目のレビューステップで検証された、蒸留された結論である。

今日、Gandalf CLI でデフォルトで使用している分布は次のとおりだ:偵察用の Beorn、ここではノイズの多い観察に耐性のある安価なモデルが欲しい。計画用の Gandalf、ここでは利用可能な最も繊細な推論が必要で、レイテンシを支払うことを受け入れる。実行用の Gwaihir、ここではスループットとツールフィードバックへの迅速な対応を優先する。それぞれに適応されたプロンプトシステムがあり、最も重要なのは、独自の制限されたツールボックスを持っていることだ。Beorn はエクスプロイトを呼び出すことはできない。Gwaihir はスコープを変更することはできない。

後者は意図的なものだ。フェーズごとにツールボックスを制限することは、ツール幻覚を切るために私たちが見つけた最も安価な方法だ。モデルは見えないものを発明しない。

トレースされた例

サンプルとして役立つようにしよう。Beorn エージェントは、これでホスト上での偵察フェーズを終了する:

Thought: He completado el escaneo de servicios. El host expone
SSH (22), HTTP (80), HTTPS (443) y un Jenkins en 8080 con
header X-Jenkins: 2.346.1. Ese build tiene CVE-2024-23897
(arbitrary file read pre-auth). Esto es relevante.

Action: emit_artifact({
  "phase": "recon",
  "host": "10.42.0.17",
  "services": [...],
  "candidate_cves": ["CVE-2024-23897"],
  "confidence": "high",
  "evidence": "banner X-Jenkins: 2.346.1"
})

Observation: artifact_id=art_8c1a stored. handoff ready.

アーティファクトは Gandalf に渡され、計画に入る。Gandalf は Beorn の完全なトレースを見ない。アーティファクト、エンゲージメントのルール、そして現在のインベントリだけを受け取る。その仕事はベクトルを決定することだ。

Thought: CVE-2024-23897 permite leer ficheros arbitrarios
sin autenticación vía el CLI de Jenkins. El objetivo del
cliente es validar acceso a credenciales. La ruta natural
es leer /var/jenkins_home/secrets/master.key y luego
credentials.xml. Confianza alta, ruido bajo. Acepto.

Action: emit_plan({
  "vector": "cve-2024-23897-file-read",
  "steps": [
    {"id": 1, "tool": "jenkins-cli-cve", "target": "10.42.0.17:8080",
     "args": {"file": "/var/jenkins_home/secrets/master.key"}},
    {"id": 2, "tool": "jenkins-cli-cve", "target": "10.42.0.17:8080",
     "args": {"file": "/var/jenkins_home/credentials.xml"}}
  ],
  "abort_if": ["http_status != 200", "response_size < 16"]
})

Observation: plan_id=pln_2f9b validated. handoff to execution.

Gwaihir は計画を取り、ステップごとに実行し、生の正規化された証拠を返す。ステップが失敗した場合、即興で対処しない:再計画のために Gandalf に制御を返す。その決定、実行エージェントに即興で対処させないことは、私たちの偽陽性を最も減らしたものの1つだ。

フェーズ間の相互検証

あるエージェントの出力と次のエージェントの入力の間に、元のエージェントのものではない検証ステップを挿入する。重要なアーティファクト(候補 CVE、見つかった資格情報、悪用の決定)については、2番目のモデル、通常はクリティカルモードの同じ Gandalf が、アーティファクトが適切に形成されているか、証拠が結論を正当化するか、次のステップがスコープから外れないかをレビューする。

トークンでは高価だ。それを受け入れる。最初に試した代替案、エージェント自体の自己整合性を信頼することは、機能しなかった。モデルは自分自身に同意する傾向がある。

既存のものとの比較

根本的に新しいものは何も発明しなかった。私たちがしたことは、さまざまな場所からのピースを混ぜることだった。

純粋な Chain-of-Thought(Wei et al., 2022)に比べて、世界に対して行動し検証する能力を得る。CoT 自体は閉じた問題には良いプロンプトだが、外部フィードバックのあるキャンペーンには良くない。

Tree of Thoughts(Yao et al., 2023)[4]に比べて、簡略化する。ToT はバックトラッキングを伴う複数の推論ブランチを並行して探索することを提案する。エレガントだが、ブランチがインフラストラクチャに対して実際のスキャナーを起動することを意味する場合、トークンとレイテンシのコストは法外だ。私たちが採用したのは、コミットする前に候補を評価するという考え方だ。

単一のエージェントで完全な自律性を追求する AutoGPT とその子孫に比べて、私たちは反対の方向に進む。分野のレビュー(Wang et al., 2023)[5]は明確だ:モノリシック自律エージェントはループにはまり、予算を燃やし、糸を失う。規律あるハンドオフを伴うマルチエージェントは、はるかに多くの予測可能性を与えてくれる。

PentestGPT(Deng et al., 2023)に比べて、サブタスクによるモジュール化とコンテキスト分離の考え方を共有する。主な違いは、私たちはアーティファクトを型付けし、相互検証を強制することで、モジュール間のハンドオフが自由テキストになることを許さないことだ。

正直なトレードオフ

解決せず、管理するだけの3つの永続的な緊張がある。

1つ目は レイテンシ対精度 だ。各ハンドオフは秒を追加する。各相互検証はもっと。モノリシックエージェントが15分で処理するキャンペーンが、私たちには40分かかる。それでいいと判断した。クライアントは待って、幻覚のないレポートを受け取ることを好む。

2つ目は トークン予算 だ。相互検証は重要な決定に対して2回支払うことを意味する。低リスクのフェーズには安いモデルを使い、高価なモデルは計画用に予約することで補う。そのセグメンテーションがなければ、エンゲージメントごとのコストは持続不可能になる。

3つ目は debugging だ。キャンペーンが失敗したとき、どのエージェントがどのコンテキストで何を決定したかを再構築する必要がある。完全な Thought/Action/Observation トレースをログに記録することは交渉の余地がない。それなしではポストモーテムはできない。しかし、それは法外な量のディスクを占める。

先月の失敗

まだ熱い、最近の例で締めくくる。ウェブペリメーターに対するキャンペーン。Beorn は Server: Werkzeug/3.0.1 Python/3.11.4 ヘッダーで応答するエンドポイントを検出した。そのアーティファクトは「デバッグモードの Flask の可能性」を中信頼度でマークした。Gandalf は計画で、PIN 保護されたデバッガを期待して /console パスを試すことを決定した。

そこまでは完璧。問題は Gwaihir に来た。ツールボックスには内部ブランチで非推奨としてマークされていたが、まだ選択可能だった werkzeug-debugger-pin-bruteforce モジュールがあった。Gwaihir はそれを選択し、モジュールは WAF が乱用としてマークするレートでリクエストを開始し、エンゲージメントの途中で送信元 IP を閉じられた。

根本原因はモデルのものではなかった。ツールボックスのものだった。ツールレジストリには、ローダーが尊重していない deprecated: true フラグがあった。ローダーの3行の変更と回帰テスト。しかし、その失敗はもっと深いことを教えてくれた:フェーズ間の相互検証はツールボックスが正しいと仮定する。ツールボックスが嘘をつくと、オーケストレーター全体が嘘をつく。

それ以来、各エージェントを起動するときに追加のチェックがある:呼び出すことができるツールをリストし、ログに印刷し、新しいクライアントに対して初めて実行するとき、人間がそのセットに署名する。エレガントではない。しかし、本番が危険にさらされているとき、どんなにうまくオーケストレーションされていても、1組の目に取って代わるエージェントはない。

Thought/Action/Observation ループは、LLM が世界と話すために私たちが知っている最高のプリミティブのままだ。実際のキャンペーンで変わるのは、ループの間に何が起こるかだ。そこで勝つか負けるかが決まる。

参考文献

  1. Yao, S. et al. (2022). ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629.
  2. Wei, J. et al. (2022). Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. arXiv:2201.11903.
  3. Deng, G. et al. (2023). PentestGPT: An LLM-empowered Automatic Penetration Testing Tool. arXiv:2308.06782.
  4. Yao, S. et al. (2023). Tree of Thoughts: Deliberate Problem Solving with Large Language Models. arXiv:2305.10601.
  5. Wang, L. et al. (2023). A Survey on Large Language Model based Autonomous Agents. arXiv:2308.11432.