
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