mcpField
R
review-attack-path

review-attack-path

github.com/subimagesecセキュリティ052 回インストール2 日前に更新
リポジトリ

review-attack-path について

review-attack-path は、SubImage を使うセキュリティ分析担当者向けのスキルです。既知の攻撃パスの確認、アセットからの関連パス検索、未モデル化の次の遷移の調査を行います。確定した証拠、推論、仮説を区別し、必要に応じて未保存のシナリオシミュレーションを重ねます。

主な機能

  1. 01既知の攻撃パスを順に確認
  2. 02アセットに関係するパスを検索
  3. 03未モデル化の次の遷移を調査
  4. 04プレビュー専用のシナリオを実行
  5. 05証拠の段階に応じて結論を分類
  6. 06チケット作成や通知送信

活用シーン

  1. 01攻撃パス終端のアセットへの影響を評価
  2. 02アセットが関わる攻撃パスを確認
  3. 03パス終端からの横移動を調査
  4. 04侵害を仮定したパスをシミュレーション

MCP 対応

subimageRunCypher などの SubImage ツールでグラフデータとスキーマを調べ、シナリオシミュレーション、チケット、通知のツールも利用します。SubImage の MCP ツールを使うワークフロー向けです。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/subimagesec/skills --skill review-attack-path

インストール後、Claude Code で/review-attack-pathを入力して読み込まれたか確認します。

詳細

攻撃経路をレビューする

概要

SubImage の攻撃経路エンジンと、基盤となる Neo4j グラフを使った 3 つのモードです。

  1. 既知の経路をたどる:平易な言葉で説明し、最も機密性の高い終端アセットを示して、最速の修正方法を提案します。
  2. アセットからピボットする:そのアセットが含まれる経路を見つけ、最も深刻なものをレビューします。
  3. n+1 の拡張を探索する:経路の終端ノードを越えてグラフを調査し、エンジンがまだモデル化していない、次に進み得る有力なステップを見つけて、候補となる遷移としてインラインで提示します。

上記のいずれにも、オプションで what-if シミュレーションを重ねて表示できます。

使用するタイミング

✅ ユーザーが SubImage UI の攻撃経路 ID、または一覧レスポンスを持っている場合。
✅ ユーザーがアセットを指定し、それが何らかの経路に含まれているか知りたい場合。
✅ ユーザーが既存の経路を拡張したい場合:「ここから横方向の移動を探して」「 の影響範囲を評価して」「次に何が起こり得る?」
✅ ユーザーが what-if を知りたい場合:「攻撃者が X を侵害したら、何が起こる?」
✅ ユーザーが subimage-mcp:investigate-cve を実行し、ピボットを確認した直後の場合。

❌ ユーザーが経路の全カタログを求めている場合:(:AttackPath:Signal) を一覧表示し、ステップごとにたどらずに要約してください。
❌ ユーザーがエンジンの背後にある静的ルールセットを求めている場合:調査結果については subimage-mcp:triage-new-findings を使うか、ドキュメントを参照してください。

前提条件

経路、そのステップ、n+1 プローブはいずれも subimageRunCypher を通じてグラフを読み取ります。このツールは、スキーマの探索に使用する subimageListModuleSchemaNodes と subimageGetNodesSchema によって支えられています。what-if シミュレーションでは subimageGetScenarioCapabilities の後に subimageCreateAttackPathScenario を使用し、グラフの読み取りではなくエンジンを実行します。チケット作成や通知などの後続処理では subimageListLinearTeams、subimageCreateTicket、subimageSendNotification を使用します。

必須入力

エントリーモードを 1 つ選び、該当する値を収集してください。不足している情報があれば、必ずユーザーに明示的に確認してください。

エントリーモード必須の値不足している場合に確認する内容
既知の経路をたどる<ATTACK_PATH_ID>「攻撃経路 ID を教えてください。SubImage で経路を表示したときの URL で確認できます。または、Cypher で有効な経路を一覧表示して 1 つ選んでください(モード A を参照)。」
アセットからピボットする<ASSET_ID>(リソース ARN または完全修飾 ID)「アセット ID を教えてください。AWS の場合はリソース ARN、GCP/Azure の場合は完全修飾 ID が使えます。SubImage の詳細ページ URL でも構いません。」
n+1 拡張を探索する開始点となる <ATTACK_PATH_ID>、または関心のある終端ノードだと判明している <ASSET_ID>「n+1 の探索はどこから始めますか。特定の経路の終端ノードからですか、それとも侵害済みとして扱うアセットからですか?」

what-if シナリオの場合は、次の値も収集してください。

値不足している場合に確認する内容
<NODE_LABEL>「開始点となるノードラベルは何ですか?(例:AWSEC2Instance、AWSS3Bucket、User)。」
付与する機能「どの攻撃者の機能をシミュレートしますか?まず subimageGetScenarioCapabilities を実行し、このノードラベルで有効な機能を確認してください。」

基本方針(ツールを呼び出す前に読むこと)

グラフを信頼できる情報源としてください。慎重に判断します。

  1. すべての出力で、確信度を次の 3 段階に分けてください。各記述がどの段階に属するかは、配置するセクションで暗黙に示します。
    • 確認済み:攻撃経路エンジン、または特定のラベル付き行にヒットした Cypher クエリから直接返された情報です。
    • 推論:文書化されたスキーマ上の関係に基づいて 2 つの確認済み事実を結び付けたもので、その結合を裏付ける Cypher プローブがあります。
    • 仮説:what-if シナリオの結果、または具体的な行による裏付けがないグラフ構造の観察です。必ずその旨を明記してください。
  2. ラベル、関係、遷移、セキュリティ上の意味を決して作り出さないでください。スキーマに関係が示されていない場合、その経路は存在しません。
  3. グラフ上で隣接しているからといって、悪用可能とは限りません。2 つのノード間の関係は必要条件ですが、十分条件ではありません。
  4. 根拠の弱い案を報告するくらいなら、見逃すことを優先してください。不適切な遷移の報告は、何も報告しないよりも望ましくありません。
  5. 根拠が弱い、または曖昧な場合は、その旨を明示してください。不確実性を覆い隠してはいけません。

ワークフロー

モード A:既知の攻撃パスをたどる
  1. パスの順序付きステップを読みます。攻撃パスは :AttackPath:Signal であり、 ステップは位置情報によってそのパスに関連付けられています。

    MATCH (a:AttackPath:Signal {id: '<ATTACK_PATH_ID>'})-[h:HAS_STEP]->(s:AttackPathStep)
    OPTIONAL MATCH (s)-[:FROM]->(src)
    OPTIONAL MATCH (s)-[:TO]->(dst)
    RETURN a.title AS title, a.criticality_score AS criticality,
           h.position AS position, s.transition_id AS transition,
           s.capability AS capability, s.description AS description,
           collect(DISTINCT src.id) AS from_ids, collect(DISTINCT dst.id) AS to_ids
    ORDER BY position
    

    レンダリング済みのステップテキストである s.description を使用します。s.templated_description には未レンダリングのプレースホルダーが残っているため、表示には使用しません。この結果は 構造上、1つのパスのステップに限定されるため、LIMIT は不要です。

    ここでは、ステップに表示される両端のノードであり、パス ID もすでに分かっているため、 FROM|TO が正しい結合方法です。アセットに関わるパスを検索する場合には使いません。モード B を参照してください。

  2. パスを次の4つの要素に分解します。

    • 初期侵害:侵入口となるノードと、攻撃者を侵入させる要因。
    • 主要なピボット:攻撃者の立場を実質的に変化させる1~3つの遷移(権限の獲得、ネットワークへの到達範囲、データへのアクセス)。
    • 終端ノード:チェーンの最後のノード。
    • 最終的な影響:攻撃者が現在掌握しているもの、または実行できること。
  3. 各遷移を平易な文章に訳します(「X から、<遷移の理由>によって攻撃者は Y へとピボットします」)。JSON を再掲しないでください。

  4. 影響の優先順位を付けます。本番環境/顧客データ/特権 ID > 社内/開発環境 > サンドボックス。

モード B:アセットを起点にピボットする
  1. アセットに関わるパスを検索します。アセットは AttackerCapacity を介して関与するため、 REASON|ON で容量に結合し、GRANTS でステップに戻る結合を行います。

    MATCH (n {id: '<ASSET_ID>'})-[:REASON|ON]-(c:AttackerCapacity)
          <-[:GRANTS]-(:AttackPathStep)<-[:HAS_STEP]-(a:AttackPath:Signal)
    WHERE coalesce(a.status, 'active') IN ['active', 'accepted']
    RETURN DISTINCT a.id AS id, a.title AS title, a.criticality_score AS criticality,
           a.context_type AS context, coalesce(a.status, 'active') AS status
    ORDER BY criticality DESC, id
    LIMIT 20
    

    これを (:AttackPathStep)-[:FROM|TO]->(n) に書き換えないでください。 FROM と TO は、ステップに「表示される」端点を保持する派生エッジです。TO はステップ自身の容量から取得され、FROM は前のステップの容量から取得されます。アセットは、端点にならなくても容量を保持する REASON になり得ます。端点を照合すると、まさにそうしたアセットは「パスなし」と報告されます。端点による結合は、すでに分かっているパスをレンダリングする場合には適切です。モード A ではこれを行いますが、パスを発見する場合には適切ではありません。

    ステータスに対する coalesce は、念のための余分な処理ではありません。パスの Signal に status プロパティがない場合があり、その場合もアクティブとして扱われます。

    context_type は絞り込みに使わず、返します。これはプロダクト独自の検索方法に合わせたものです。ユーザーが環境全体の状況を求めている場合、または同じパスがコンテキストごとに返される場合は、'default' に絞り込みます。

  2. 結果が空の場合:「現時点で、<ASSET_ID> に関わる既知の攻撃パスはありません。エンジンがモデル化していないパスを通じて、アセットが依然としてリスクにさらされている可能性はあります。このアセットを起点に、n+1 拡張ハント(モード C)を実行しますか?」この説明が妥当なのは、上記の容量結合を使った場合に限られます。

  3. 結果が空でない場合:上位1~3件を選び、それぞれに対してモード A を実行します。

モード C:n+1 拡張の探索

分析モードで追加される機能です。パスの終端ノードを調べ、グラフを探索して、エンジンがまだモデル化していない、信頼できる次のステップが存在するかを確認します。

  1. 拡張するパスの終端ノードを選びます(ユーザーが <ASSET_ID> を指定した場合は、それを直接使用します)。
  2. スキーマを確認し、そのノードの隣に何があるとされているかを調べます。
    subimageListModuleSchemaNodes(module="<module-of-the-node>")
    subimageGetNodesSchema(node_names=["<TerminalLabel>"])
    
    リレーションシップの種類と対象ラベルを記録します。
  3. 妥当なリレーションシップごとに、小規模な Cypher クエリを実行して、実際の行が存在するか確認します(以下の Cypher ルールを参照)。信頼できる n+1 カテゴリの例:
    • ラテラルムーブメント:侵害されたコンピュートインスタンスから、セキュリティグループ/VPC ピアリング/共有サービスアカウントを介して到達可能なピア。
    • 権限昇格:非管理者 ID から、実質的に管理者権限を付与するロールやポリシーへのエッジ(iam:PassRole、AssumeRole、グループメンバーシップ、ワイルドカード権限を持つカスタムロール)。
    • データ流出:侵害されたあらゆるコンピュートから、機密データのラベルが付いている、またはパブリック読み取り可能なストレージへの到達。
    • クロスアカウント/クロステナント:信頼関係、クロスアカウントのロール引き受け、フェデレーション ID ブリッジ。
  4. 報告する前に、以下の5項目チェックリストを適用します。
  5. 信頼できる拡張をそれぞれ出力に示します(以下の形式)。カウンターを増やし、最終出力でその値に言及します。
モード D:what-if シナリオ(オーバーレイ)

ユーザーが「X が侵害されたらどうなるか」「Y の付与をシミュレートして」などと尋ねた場合に限ります。

  1. subimageGetScenarioCapabilities(node_label="<NODE_LABEL>") を実行し、有効なケイパビリティを確認します。
  2. 利用可能なケイパビリティをユーザーに伝え、どれを付与するか尋ねます。
  3. subimageCreateAttackPathScenario(..., preview_only=true) を実行します。必ず preview_only=true を渡してください。これにより、ユーザーは結果を実際のパスとして永続化せずに確認できます。
  4. モード A を使って、生成されたパスをたどります。

グラフ探索の Cypher ルール

以下は、モード C の探索に加えて、上記のモード A およびモード B のクエリにも適用されます。

  • subimageRunCypher はクエリパラメーターを受け取らないため、<ATTACK_PATH_ID> と <ASSET_ID> はリテラルとして埋め込みます。置換する前にバックスラッシュとシングルクォートをエスケープしてください。ARN や完全修飾された GCP/Azure ID にはどちらも含まれることがあり、エスケープされていないアポストロフィはステートメントを壊します。
  • ツールが返す行数は、LIMIT に指定した値にかかわらず最大 100 行で、真の total_count も併せて報告されます。ここで扱う内容には十分な数ですが、探索で上限に達した場合はページサイズではなく total_count を報告してください。
  • 攻撃パスのリレーションシップ一覧は固定されています。ステップには <-[:HAS_STEP]-、-[:GRANTS]->、-[:FROM]->、-[:TO]-> があり、ケイパビリティには <-[:GRANTS]-、-[:ON]->、-[:REASON]-> と、他のケイパビリティへの REQUIRED があります。(:AttackPathStep)-[:ON]-> や (:AttackPathStep)-[:REASON]-> はありません。この 2 つはケイパビリティに接続します。
  • 探索クエリには常に上限を設定してください:LIMIT 5。広範なスキャンはトークンを消費するだけで、新たな情報は得られません。
  • 広く確認してから絞り込みます。最初のクエリで構造の存在を確認し、2 つ目のクエリで具体的な行を取得します。
  • テキストを照合する場合は、正規表現より toLower(prop) CONTAINS "term" を優先します。
  • 上限のない可変長パス(-[*]->)は使用しないでください。最大でも -[*1..3]-> のように範囲を限定します。
  • 推論する前に、実際の行でラベルとリレーションシップの存在を確認してください。MATCH (a:Foo)-[:BAR]->(b) の結果が 0 行なら、そのリレーションシップは概念上のものです。悪用可能なものとして提示しないでください。
  • subimageRunCypher が 0 行を返した場合、それ自体が確認済みの事実です。このテナントにはそのリレーションシップは存在しません。

候補となる拡張を提示する条件

次の5つすべてを満たす場合にのみ、候補を出力に含めてください。

  1. ソースラベルとターゲットラベルが、このテナントのグラフ内に実際の行として存在する(スキーマにあるだけではない)。
  2. 両者の関係がグラフ内に存在する(Cypher のプローブで1行以上が見つかっている)。
  3. 攻撃者の現在の位置から、そのステップを実行できると妥当に考えられる(能力、ネットワーク到達範囲、ID)。
  4. そのステップによって、意味のある新たな影響、権限、または到達範囲が得られる(「同じ種類の別のノード」に移るだけではない)。
  5. 既存の遷移として明らかにモデル化されていない(類似したノードタイプ間の既存パスをいくつかたどって確認する)。

いずれか1つでも満たさない場合は、提示しないでください。内部で理由を記録し、次に進んでください。

候補となる拡張の形式

各候補を、次のフィールドを正確に含むサブブロックとして表示してください。

  • context: 攻撃パス分析:<ユーザーのリクエストを1文で>
  • problem: 遷移が欠落している可能性:<SourceLabel> は <relationship> を介して <TargetLabel> に到達でき、<impact> を可能にします。遷移タイプ:<lateral-movement | privilege-escalation | data-exfiltration | cross-account>。根拠:<N> 件一致。Cypher パターン:<1行の MATCH 文>。
  • workaround: partial
  • workaround_detail: 現在のパスは <terminal node> で止まっていますが、グラフの根拠は、攻撃者が <mechanism> を介して <target> に進める可能性を示しています。
  • suggested_improvement: 次の条件下で、<SourceLabel> から <TargetLabel> への遷移を追加してください:<検証済み条件のリスト>。

出力

確認した各パス(モードAおよびB)について:

# Attack path: <short title>

**Criticality**: <score / category>  ;  **Steps**: <N>

## In plain English
The attacker starts at [[entity:<Label>:<id>|<short>]] (<one-line context>).
1. <transition 1 in plain language>
2. <transition 2 in plain language>
3. ...
N. The chain ends at [[entity:<Label>:<id>|<short>]] which gives them <impact>.

## What this means
- **Initial compromise**: <one line>
- **Key pivot(s)**: <one line, naming the transitions that mattered>
- **Terminal impact**: <one line, naming what the attacker now controls>
- **Most sensitive impacted asset**: [[entity:<Label>:<id>|<short>]] (<why it matters>)
- **Why this path is exploitable today**: <rooted in the actual transitions, no speculation>

## Fastest remediation
<one or two concrete actions in priority order. Name the policy, the SG rule, the secret, the role.>

## What to do next
- Mitigate: <action>; owner: <team or `unknown`>
- Re-run after the change: SubImage refreshes paths on the next sync.

モードCの後に、次を追加してください。

## n+1 extension hunt
- probes run: <count>
- credible extensions found: <count>
- candidate extensions surfaced: <count>

<for each surfaced candidate, render the five-field sub-block from "Candidate extension format" above>

モードCで信頼できる候補が見つからなかった場合は、その旨を明確に述べてください。「グラフの根拠によって裏付けられる、信頼できる n+1 拡張はありません。終端ノード <id> は、5項目のチェックリストを通過する対象に現在隣接していません。」

チャット UI でリンクできるよう、エンティティには必ず [[entity:<Label>:<node.id>|<display>]] の形式でタグを付けてください。

任意のフォローアップ

ユーザーがチケットの作成を希望する場合:

  1. subimageListLinearTeams() を実行してチーム ID を取得します。
  2. subimageCreateTicket(team_id=..., title="Mitigate: <path title>", description="<the narrative + remediation>") を実行します。

作成前にチームを確認してください。subimageSendNotification の場合も同様に、チャンネルを確認してください。

アンチパターン

  • 生の遷移リストを Markdown の表として返すこと。チャットシステムプロンプトでツールデータに対して禁止されており、パスを読みやすくするという目的にも役立ちません。
  • 修正策を曖昧に述べること(「IAM ポリシーを確認する」など)。ポリシー、ルール、リソースを明示してください。
  • エンジンが生成していない遷移を推測すること(「攻撃者はほかにも……できる可能性があります」など)。ユーザーに求められた場合は、Cypher プローブを使ってモードCを実行してください。根拠なしに拡張を主張してはいけません。
  • 弱いプローブを1回行っただけで遷移案を報告すること。5項目のチェックリストを実行し、1つでも不合格なら報告しないでください。
  • 1回の応答で5つ以上のパスをたどること。ユーザーが対応するのは上位1つか2つだけです。それ以上は情報のノイズになります。
  • what-if シナリオで preview_only=true を指定し忘れること。フラグを付けない形式ではシナリオが保存されます。初回の確認でユーザーが望むことはほとんどありません。
  • スキーマ上の関係を、悪用可能性が確認済みであるかのように扱うこと。まずプローブを実行してから報告してください。

参考資料

  • ツールガイド(subimageReadMe で常に読み込まれます):Domain 4「攻撃パス分析」、Cypher については Domain 1「ナレッジグラフの探索」。
  • 脆弱性のトリアージに使う関連スキル(この分析に進むことがよくあります):subimage-mcp:investigate-cve。
  • アーキテクチャ:https://app.subimage.io/docs/architecture/attack_path

コメント