
keyv/cacheableサプライチェーン攻撃用スキャナー:侵害されたnpmパッケージを検出し、ペイロードハッシュを検証し、リポジトリモードとホストモードで永続化インプラントを発見します。
English | Português
keyv/cacheable サプライチェーンインシデント(「Shai-Hulud: Here We Go Again」、2026年8月4日) 用のスタンドアロンスキャナー — クラウド/CI の認証情報を窃取し、デッドマンスイッチ付きの永続化を仕込む自己増殖型ワームにより440以上の npm パッケージが侵害されました。
何もインストールせずに、数分で以下を検出します:
package-lock.json、npm-shrinkwrap.json、yarn.lock(v1 および Berry)、pnpm-lock.yaml、bun.lock 内の侵害されたパッケージ — 推移的依存関係も含め、完全なチェーン付き(例:eslint → file-entry-cache → flat-cache → [email protected]);node_modules 内のインストール済みペイロード(既知アーティファクトの名前 + SHA-256 ハッシュ);SUSPECT とマークされ、ハッシュなしでは確定されません;.claude/settings.json と .vscode/tasks.json 内のフック、一時アーティファクト(bun-dl-*);keyv/cacheable ファミリーのメンテナーアカウントが侵害されました。攻撃者は "preinstall": "node setup.mjs" フック付きの新バージョンを公開しました — これは npm install を実行した人の権限で、パッケージのインストール前に実行されるコードです。setup.mjs は GitHub から Bun ランタイムをダウンロードし、その中でペイロードを実行します — node プロセスのみを監視するツールに対する回避策です。Math_Symbol.js(約728 KB、難読化)が認証情報を窃取します:AWS インスタンスメタデータ、AWS/GCP/Azure キー、Vault トークン、Kubernetes サービスアカウント、GitHub Actions シークレット、npm トークンに加え、ディスク上の秘密鍵とベアラートークンに対する汎用の正規表現スイープ。npm install/npm ci を実行したすべての人。--ignore-scripts を使用した場合、フックは実行されませんでした。.claude/settings.json(SessionStart)と .vscode/tasks.json(folderOpen)に自動起動フックが注入されており、クローンしたフォルダを開いたときにローダーを実行します — npm install も、何のインストールも不要です。これには インシデントを調査するために リポジトリをクローンした人や、ディレクトリを開いた AI コーディングエージェントも含まれます — AI エージェントフック(.claude/)がサプライチェーンベクターとして使用された最初の公知事例の1つです。インプラントは LaunchAgent(macOS)または systemd ユーザーサービス + loginctl enable-linger(Linux)によって維持される「ウォッチャー」(gh-token-monitor)をインストールします。60秒ごとに窃取した GitHub トークンを API に対して検証します。トークンが機能している間は何も起こりません。応答が 4xx になると — つまりトークンを失効させた瞬間に — eval を介して ~/.config/gh-token-monitor/handler の内容を実行します:攻撃者がリモートで定義した任意のコマンドです。公開分析ではその内容は判明していません — データ破壊、再インプラント、ランサムウェア、あるいは何もない可能性があります。リスクは評価不能であり、だからこそ対応順序が絶対なのです。
対応を変える3つの特性:
host モードでこの点を警告します。[email protected] は SLSA アテステーションを通過した状態でリリースされました。来歴(Provenance)が証明するのは ソース ではなく ビルド の整合性です:正規のワークフローがすでにトロイの木馬化されたコードをコンパイルしていました。package.json(preinstall フック)と、パッケージに追加された2つの新しいファイル(setup.mjs、Math_Symbol.js)にあります。eslint → file-entry-cache → flat-cache → keyv です。だからこそスキャナーはすべての検出結果でチェーンを表示します。scan.mjs には以下の特性があります — サプライチェーンインシデントに対応するすべての人にとって重要です:
npm install は不要です。実行する前に15分で scan.mjs 全体を監査できます。--update(新しい IOC マニフェストのダウンロード)で、明示的かつ任意です。docker run --network=none または隔離されたマシン:scan.mjs + iocs.json をコピーするだけです。要件:Node.js ≥ 18(npm が入っているマシンならどれでも既にあります)。scan.mjs + iocs.json の2つのファイルをダウンロードするだけです:インストールは不要です。
注意: このリポジトリ全体をクローンした場合、
fixtures/フォルダにはテストで使用される不活性な IOC が含まれています(実在の名前とバージョン、ダミーコンテンツ — マルウェアではありません)。スキャナーはこれを自動的にスキップし、出力で警告します。このフォルダからの検出結果は、意図的にスキャンした場合にのみ表示されます。
異なる問いに答える2つの実行モードがあり、それが どこで 実行するかを決定します:
repo モードはロックファイルと node_modules を読み取ります — ロックファイルは git 内にあるため 集中化できます:1人で会社のすべてのリポジトリをスキャンできます。host モードはインプラント(ウォッチャー、LaunchAgent/systemd、IDE フック)を探します。これは コードが実行されたマシン上 に存在します — git にはなく、集中化できません。node scan.mjs repo /folder/with/all/the/repos --json=result.json --html=report.html
誰も関与させずに「どのプロジェクトが露出しているか」に数分で回答します。複数のパスを受け入れ、サブディレクトリを再帰的に走査します(モノレポとワークスペースを含む)。
検出結果のある各プロジェクトについて、2026-08-04 09:35 UTC 以降にそのプロジェクトに関わった人を特定します(git log、CI ログ)。その人々が自分のマシンで以下を実行します:
node scan.mjs # current directory + host, in ~30 seconds
対象範囲:(a) その期間内に npm install/npm ci を実行した人。または (b) 単にクローンして VS Code または AI エージェントでフォルダを開いただけの人 — ベクターB はインストールを必要としません。
コストが約30秒で、ファネルが漏れる可能性があるため(迷子のクローン、個人プロジェクト)、最も安全な社内メッセージは次のとおりです:すべての開発者が node scan.mjs を一度実行し、--json/--html を AppSec に送信します。送信は設計上手動です — スキャナーにはテレメトリがありません(ゼロエグレス)。
最優先事項:ここに最も価値の高い認証情報が存在します。ここでスキャナーには2つの異なる役割があります — 1つは過去用、もう1つは将来用です:
過去のトリアージ — 判断のためにランナーをスキャンしないでください。 「このランナーは感染したか?」という問いはスキャンでは答えられません:08-04 以降にいずれかのジョブが --ignore-scripts なしで影響を受けたバージョンをインストールした場合、その時点で認証情報はすでに窃取されており、ランナーのホストが証拠を保持することはほとんどありません(エフェメラルランナーはジョブ終了時にコンテナを破棄し、ウォッチャーは約24時間で自己消去します)。これに答えるのはステップ1のロックファイルと CI ログです。答えが「はい、インストールしました」の場合:ランナーを再構築し、シークレットをローテーションしてください — ランナーはエフェメラルであり、クリーンアップする理由はありません。
今後の予防 — はい、パイプラインで実行してください。 スキャナーをビルドステップとして、repo モードで、チェックアウト後かつ npm install の前に追加します。ランナーのホストを検査するのではなく、これからインストールされるコードを検査し、悪意のある preinstall が実行される機会を得る前に終了コードでビルドを失敗させます:
# example (GitHub Actions / GitLab CI — adapt):
- run: node scan.mjs repo . --json # exit 0 clean · 1 findings · 2 COMPROMISED
- run: npm ci --ignore-scripts # only runs if the previous step passed
node scan.mjs # scan the current directory + the host
node scan.mjs repo /path/a /path/b
node scan.mjs host # persistence/implants on the machine only
node scan.mjs repo . --json=result.json --html=report.html
node scan.mjs --update # update iocs.json (the only network operation)
| Level | Meaning | Action |
|---|---|---|
COMPROMISED | node_modules に悪意のあるバージョンがインストールされている、ハッシュでペイロードが確認された、または永続化インプラントが見つかった | ホストを侵害済みとして扱う。対応順序に従う — 認証情報をローテーションする前にインプラントをクリーンアップする |
EXPOSED | ロックファイルに悪意のあるバージョンが固定されている、実行の証拠はない | 安全なバージョンに固定し、node_modules を削除し、--ignore-scripts で再インストールする |
AT_RISK | 悪意のあるバージョンを許容する package.json の範囲(^/~) | 正確なバージョンに固定するか、レジストリプロキシでブロックする |
SUSPECT | ヒューリスティック(ハッシュが一致しないワームのファイル名、不審なライフサイクルスクリプト) | 手動で検査する — 新しい亜種または誤検知の可能性 |
INFO | ベクターは存在するが IOC なし(例:一般的な folderOpen タスク) | レビューする |
--html は、タイムスタンプ、ホスト名、IOC マニフェストバージョン、スキャナー自身の SHA-256 を含む自己完結型のレポートを生成します — インシデント通知への添付や監査証跡として使用できます。
まだいかなる認証情報も失効またはローテーションしないでください — それがトラップの引き金です。順序:
1. 隔離(ISOLATE) — マシンをネットワークから遮断します。安全です:HTTP 応答がなければ 4xx もなく、トラップは発動せず、外部送信も停止します。電源を切らないでください(揮発性メモリは証拠です)。
2. 保全(PRESERVE) — 何かを削除する前に(ウォッチャーは約24時間で自己破壊します):
mkdir -p /tmp/evidence && cp -r ~/.config/gh-token-monitor /tmp/evidence/ 2>/dev/null
cp /tmp/gh-token-monitor.*.log /tmp/evidence/ 2>/dev/null
shasum -a 256 /tmp/evidence/* 2>/dev/null
handler ファイルは実行されるはずだった攻撃者のコマンドです — 実行せず、シェルに貼り付けないでください。不活性なテキストとして扱ってください。started_at ファイルは露出期間の範囲を定めます(監査人と規制当局がそれを要求します)。
3. 根絶(ERADICATE) — まずウォッチャープロセスを終了し、次に:
# macOS
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
rm -f ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
# Linux
systemctl --user disable --now gh-token-monitor.service
loginctl disable-linger "$USER"
rm -f ~/.config/systemd/user/gh-token-monitor.service
# both
rm -rf ~/.config/gh-token-monitor ~/.local/bin/gh-token-monitor.sh /tmp/bun-dl-*
また、.claude/settings.json と .vscode/tasks.json から悪意のあるフックを削除し、setup.mjs/Math_Symbol.js/math_init.js ファイルを削除し、キャッシュ(~/.npm/_cacache、pnpm、yarn)をクリアします。クリーンと判定されるまで node scan.mjs host を再度実行してください。
4. ローテーション(ROTATE) — 影響を受けたすべてのホストがクリーンアップされ検証された場合のみ(ウォッチャーが1つでも生きていれば引き金を引くのに十分です)。まず npm トークンを失効させます(ワームの拡散を止めます)。次に GitHub(PAT、デプロイキー)、AWS/GCP/Azure、Vault、Kubernetes、CI シークレット — そしてディスク上にあったすべてのシークレット。なぜなら正規表現スイープがあったからです。
5. 監査(AUDIT) — ワームはあなたの名義で行動します:組織内で Shai-Hulud: Here We Go Again と説明されたリポジトリ、08-04 以降に予期せず公開された npm バージョン(非推奨にして利用者に通知)、started_at の期間内の CloudTrail/監査ログでの認証情報の使用を探してください。
その後:node_modules を削除し、クリーンなロックファイルから --ignore-scripts で再インストールします。実行が確認された CI ランナーとホスト:常にゼロから再構築 — 任意のコードが実行されたのであり、既知のアーティファクトのリストは完全性を保証しません。
規制対象機関(BR): 認証情報へのアクセスを伴う侵害が確認された場合、報告義務が発生する可能性があります(Res. CMN 4.893/2021、Res. BCB 85/2021;個人データが関与する場合は LGPD 第48条)。タイムラインを UTC で記録してください —
started_at、検知、封じ込め、根絶、ローテーション — そして法務/コンプライアンスと期限を確認してください。
インシデントは進行中で、リストは増え続けています。最新のマニフェストを取得するには:
node scan.mjs --update # the only operation that touches the network
--update は iocs.json を このリポジトリ(securest8/npm-incident-response)から取得し、第三者からは決して取得しません — Securest8 がキュレーションゲートです。ここに最後に公開されたものを取得します。
パッケージリストは 公開 Wiz フィード から取得されます。ハッシュ、C2 ドメイン、永続化 IOC、安全なバージョンは静的で、tools/gen-iocs.mjs でキュレーションされています。Wiz CSV のスナップショットは再現性とオフライン実行のために tools/keyv-packages.csv にあります。
node tools/gen-iocs.mjs # fetch the latest Wiz CSV, regenerate iocs.json + refresh the snapshot
node tools/gen-iocs.mjs --offline # regenerate from the committed snapshot, no network
node tools/gen-iocs.mjs --allow-shrink # allow a package count lower than the snapshot (guarded by default)
ジェネレーターは冪等です:既存の manifest_version を保持し、実質的な変更がない場合はファイルを書き換えず、空または縮小されたマニフェストの書き込みを拒否します(切り詰められた/変更された上流フィードに対する防御)。
自動化: .github/workflows/update-iocs.yml はジェネレーターを毎日(およびオンデマンドで)実行し、IOC が実際に変更された場合のみ main にコミットします — そのため利用者の --update は約1日以内に Wiz フィードを追跡し、完全な履歴はコミットで監査可能です。インシデントが沈静化し、代わりに手動マージを希望する場合は、プルリクエストステップ(ワークフローに記載)に切り替えてください。
node test/run-tests.mjs # 15 assertions against fixtures/demo-repo
fixtures/demo-repo は 不活性な IOC を含むテストリポジトリです(実在の名前とバージョン、ダミーコンテンツ — マルウェアではありません)。スキャナー自身のリポジトリをスキャンする場合、fixtures/ フォルダは自動的にスキップされます(出力に警告が含まれます)。テストではパスを明示的に渡してスキャンします。
単一インシデント専用のツールで、攻撃の活動期間中の迅速なトリアージのために構築されました — Socket、Snyk などの代替品ではありません。調査と IOC:Socket.dev、Wiz Research(公開 CSV)、Kodem Security。
Securest8 によってメンテナンスされています。MIT ライセンス。