review-attack-path について
review-attack-path は、SubImage を使うセキュリティ分析担当者向けのスキルです。既知の攻撃パスの確認、アセットからの関連パス検索、未モデル化の次の遷移の調査を行います。確定した証拠、推論、仮説を区別し、必要に応じて未保存のシナリオシミュレーションを重ねます。
主な機能
- 01既知の攻撃パスを順に確認
- 02アセットに関係するパスを検索
- 03未モデル化の次の遷移を調査
- 04プレビュー専用のシナリオを実行
- 05証拠の段階に応じて結論を分類
- 06チケット作成や通知送信
活用シーン
- 01攻撃パス終端のアセットへの影響を評価
- 02アセットが関わる攻撃パスを確認
- 03パス終端からの横移動を調査
- 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 つのモードです。
- 既知の経路をたどる:平易な言葉で説明し、最も機密性の高い終端アセットを示して、最速の修正方法を提案します。
- アセットからピボットする:そのアセットが含まれる経路を見つけ、最も深刻なものをレビューします。
- 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 を実行し、このノードラベルで有効な機能を確認してください。」 |
基本方針(ツールを呼び出す前に読むこと)
グラフを信頼できる情報源としてください。慎重に判断します。
- すべての出力で、確信度を次の 3 段階に分けてください。各記述がどの段階に属するかは、配置するセクションで暗黙に示します。
- 確認済み:攻撃経路エンジン、または特定のラベル付き行にヒットした Cypher クエリから直接返された情報です。
- 推論:文書化されたスキーマ上の関係に基づいて 2 つの確認済み事実を結び付けたもので、その結合を裏付ける Cypher プローブがあります。
- 仮説:what-if シナリオの結果、または具体的な行による裏付けがないグラフ構造の観察です。必ずその旨を明記してください。
- ラベル、関係、遷移、セキュリティ上の意味を決して作り出さないでください。スキーマに関係が示されていない場合、その経路は存在しません。
- グラフ上で隣接しているからといって、悪用可能とは限りません。2 つのノード間の関係は必要条件ですが、十分条件ではありません。
- 根拠の弱い案を報告するくらいなら、見逃すことを優先してください。不適切な遷移の報告は、何も報告しないよりも望ましくありません。
- 根拠が弱い、または曖昧な場合は、その旨を明示してください。不確実性を覆い隠してはいけません。
ワークフロー
モード A:既知の攻撃パスをたどる
-
パスの順序付きステップを読みます。攻撃パスは
: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 を参照してください。 -
パスを次の4つの要素に分解します。
- 初期侵害:侵入口となるノードと、攻撃者を侵入させる要因。
- 主要なピボット:攻撃者の立場を実質的に変化させる1~3つの遷移(権限の獲得、ネットワークへの到達範囲、データへのアクセス)。
- 終端ノード:チェーンの最後のノード。
- 最終的な影響:攻撃者が現在掌握しているもの、または実行できること。
-
各遷移を平易な文章に訳します(「
Xから、<遷移の理由>によって攻撃者はYへとピボットします」)。JSON を再掲しないでください。 -
影響の優先順位を付けます。本番環境/顧客データ/特権 ID > 社内/開発環境 > サンドボックス。
モード B:アセットを起点にピボットする
-
アセットに関わるパスを検索します。アセットは
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'に絞り込みます。 -
結果が空の場合:「現時点で、
<ASSET_ID>に関わる既知の攻撃パスはありません。エンジンがモデル化していないパスを通じて、アセットが依然としてリスクにさらされている可能性はあります。このアセットを起点に、n+1 拡張ハント(モード C)を実行しますか?」この説明が妥当なのは、上記の容量結合を使った場合に限られます。 -
結果が空でない場合:上位1~3件を選び、それぞれに対してモード A を実行します。
モード C:n+1 拡張の探索
分析モードで追加される機能です。パスの終端ノードを調べ、グラフを探索して、エンジンがまだモデル化していない、信頼できる次のステップが存在するかを確認します。
- 拡張するパスの終端ノードを選びます(ユーザーが
<ASSET_ID>を指定した場合は、それを直接使用します)。 - スキーマを確認し、そのノードの隣に何があるとされているかを調べます。
リレーションシップの種類と対象ラベルを記録します。subimageListModuleSchemaNodes(module="<module-of-the-node>") subimageGetNodesSchema(node_names=["<TerminalLabel>"]) - 妥当なリレーションシップごとに、小規模な Cypher クエリを実行して、実際の行が存在するか確認します(以下の Cypher ルールを参照)。信頼できる n+1 カテゴリの例:
- ラテラルムーブメント:侵害されたコンピュートインスタンスから、セキュリティグループ/VPC ピアリング/共有サービスアカウントを介して到達可能なピア。
- 権限昇格:非管理者 ID から、実質的に管理者権限を付与するロールやポリシーへのエッジ(
iam:PassRole、AssumeRole、グループメンバーシップ、ワイルドカード権限を持つカスタムロール)。 - データ流出:侵害されたあらゆるコンピュートから、機密データのラベルが付いている、またはパブリック読み取り可能なストレージへの到達。
- クロスアカウント/クロステナント:信頼関係、クロスアカウントのロール引き受け、フェデレーション ID ブリッジ。
- 報告する前に、以下の5項目チェックリストを適用します。
- 信頼できる拡張をそれぞれ出力に示します(以下の形式)。カウンターを増やし、最終出力でその値に言及します。
モード D:what-if シナリオ(オーバーレイ)
ユーザーが「X が侵害されたらどうなるか」「Y の付与をシミュレートして」などと尋ねた場合に限ります。
subimageGetScenarioCapabilities(node_label="<NODE_LABEL>")を実行し、有効なケイパビリティを確認します。- 利用可能なケイパビリティをユーザーに伝え、どれを付与するか尋ねます。
subimageCreateAttackPathScenario(..., preview_only=true)を実行します。必ずpreview_only=trueを渡してください。これにより、ユーザーは結果を実際のパスとして永続化せずに確認できます。- モード 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つすべてを満たす場合にのみ、候補を出力に含めてください。
- ソースラベルとターゲットラベルが、このテナントのグラフ内に実際の行として存在する(スキーマにあるだけではない)。
- 両者の関係がグラフ内に存在する(Cypher のプローブで1行以上が見つかっている)。
- 攻撃者の現在の位置から、そのステップを実行できると妥当に考えられる(能力、ネットワーク到達範囲、ID)。
- そのステップによって、意味のある新たな影響、権限、または到達範囲が得られる(「同じ種類の別のノード」に移るだけではない)。
- 既存の遷移として明らかにモデル化されていない(類似したノードタイプ間の既存パスをいくつかたどって確認する)。
いずれか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>]] の形式でタグを付けてください。
任意のフォローアップ
ユーザーがチケットの作成を希望する場合:
subimageListLinearTeams()を実行してチーム ID を取得します。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