← すべての投稿に戻る
公開日 · 著者 Renaud Deraison

トークンの失効が引き金だった

10 月 8 日、Shai-Hulud ワームを仕込んだ tensorlake 0.5.144 が npm に公開されました。GitHub、npm、クラウド、SSH の認証情報を盗み、被害者のリポジトリに .claude/settings.json を仕込み、盗んだ GitHub トークンを 60 秒ごとに確認して、それが使えなくなった瞬間に rm -rf ~/ を実行するモニターをインストールします。インシデント対応の定石の第一歩であるトークンの失効が、被害者のホームディレクトリを消し去るのです。Bromure Agentic Coding では、デフォルトの経過日数ゲートがこのバージョンを npm から隠し、マシン内の GitHub トークンは最初から本物ではありませんでした。

ワームは、見つけた人に向けた文字列をコードに残していました。 「IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner」。このトークンを失効させれば、 持ち主のコンピューターを消去する、という意味です。コードは書いてあるとおりに動きます。

10 月 8 日(水)01:12 UTC、tensorlakeai/tensorlake のリリースワークフローが tensorlake SDK のバージョン 0.5.144 を npm に公開しました。Tensorlake は AI エージェント向けのサーバーレス・サンドボックス基盤で、その npm パッケージは週におよそ 12,000 回ダウンロードされています。新しいバージョンはプロジェクト自身のリポジトリから、 プロジェクト自身のワークフローでビルドされたもので、npm には来歴(provenance)の証明も 表示されていました。

Socket は 11 分後にこれを検知しました。 StepSecurity、 Aikido、 The Hacker News も同じ日に続きました。このバージョンには Shai-Hulud ワームの新しい亜種が入っていて、 今回は人質を取ります。

どうやって入り込んだか

StepSecurity がタイムラインを再構成しています。10 月 7 日 01:20 UTC、何者かがメンテナー の名前でリポジトリのメインブランチに直接コミットを push しました。その後数時間でさらに 7 件の コミットが続きました。誰も気づかず、翌日リリースワークフローが main のコードをビルドして 公開しました。来歴の証明は正確でした。パッケージがどこでビルドされたかを証明していて、その 場所はすでに攻撃者の手に落ちていたのです。

パッケージの package.json には 1 行が加わっていました。

"preinstall": "node lib/setup.mjs"

npm は、パッケージを所定の場所に展開するより前に preinstall を実行します。このスクリプト は Bun ランタイムをダウンロードし、それを使って難読化された 856 KB のペイロード lib/Math_Symbol.js を実行します。CI 上では何もしません。StepSecurity のまとめは 「狙いは開発者のマシン」です。

何を盗み、どこへ送るか

ペイロードは、GitHub と npm のトークン、AWS・Azure・GCP のクラウドキー、Kubernetes と HashiCorp Vault のシークレット、SSH キー、.env ファイル、ブラウザーに保存されたログイン 情報、暗号資産ウォレット、そして AI コーディングツールの設定(.claude、.cursor、 .kiro、Windsurf、Zed)を集めます。集めたものを暗号化して iseekaigogo.com に送り、 失敗すれば被害者自身の GitHub アカウントに公開リポジトリを作ってそこに置きます。説明文は 「Shai-Hulud: Here We Go Again.」です。ドメインが落ちると、約 30 の公開 RPC エンドポイント を通じて Ethereum のコントラクトから新しいドメインを探します。

そして広がります。被害者の npm トークンで、その人のパッケージを一つずつダウンロードし、 自分を追加し、バージョンを上げて再公開します。GitHub トークンでは、 .claude/settings.json と .vscode/tasks.json を被害者のリポジトリにコミットします。 コミット者は [email protected]、メッセージは chore: update dependencies です。そのプロジェクトを次に Claude Code や VS Code で開いた 開発者が、ワームをもう一度実行することになります。StepSecurity の Ashish Kurmi が言うとおり、 このマルウェアは「誰かが Claude Code や VS Code でプロジェクトを開くと再び動く」のです。

人質

新しいのは gh-token-monitor というサービスです。Linux では ~/.config/systemd/user/ の systemd ユーザーユニット、macOS では ~/Library/LaunchAgents/com.user.gh-token-monitor.plist の LaunchAgent、Windows では monitor.ps1 を実行するログオンタスクです。StepSecurity はその動作をこう説明しています。

60 秒ごとに、最大 24 時間、サービスはそのトークンを GitHub API で確認する。GitHub が それを拒否すると、サービスは rm -rf ~/ を実行する。

あなたが被害者だとしましょう。インストールしたパッケージが GitHub トークンを盗んだと知り ます。定石では、すぐに失効させることになっています。失効させると、1 分以内にホームディレクトリ が消えます。チェックアウト、コミットしていない作業、dotfiles、そしてワームが何をしたかの 証拠まで。Socket は、その脅しがコードの中にリテラル文字列として書かれているのを見つけました。 IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner。

そのため、各社のアドバイザリーには手順の順番に関するルールが付くようになりました。 StepSecurity のステップ 3 は、モニターを見つけて削除すること、そしてその前に失いたくない ものをすべてバックアップすることです。太字の警告はこうです。「ステップ 3 が終わるまで、 GitHub トークンを失効させないでください」。Socket も同じことを言っています。モニター探しに かかる 1 時間は、攻撃者があなたの push 権限付きの有効なトークンを握っている 1 時間です。

ノート PC では:定石の第一歩が消去を引き起こす1. npm install[email protected]preinstall: node lib/setup.mjs本物を盗むGitHub トークン(push 権限)npm トークン(公開権限)~/.aws ~/.kube ~/.sshインストールするものgh-token-monitor (LaunchAgent)リポジトリに .claude/settings.json2. モニター、60 秒ごとGET api.github.com/userAuthorization: 本物のトークン200 OK → 待ち続ける攻撃者はあなたの push 権限を握ったまま401 → ハンドラーを実行3. トークンを失効させるどの定石もそう言うとおりにrm -rf ~/チェックアウト、未コミットの作業、dotfiles、そして証拠
ノート PC では盗まれたトークンが本物なので、失効は急を要します。モニターはその失効を、開発者のホームディレクトリの消去に変えてしまいます。

Bromure のマシンで同じインストールをすると

Bromure Agentic Coding では、Claude Code や Codex などのエージェントが Mac 上の Linux VM で動きます。マシン 1 台につき VM 1 台です。VM が開く接続は、npm の取得も含めてすべて Mac 上のプロキシを通ります。そうしたマシンの 1 台で 10 月 8 日を再現してみましょう。エージェント がプロジェクトに tensorlake を追加し、npm install を実行します。

そのバージョンはまだ存在しなかった

新しいマシンでは、サプライチェーンの経過日数ゲートが最低 2 日の設定で有効になっています。 npm がレジストリに tensorlake のバージョン一覧を求めると、プロキシは公開から 2 日未満の バージョンをすべて取り除き、latest を十分に古い最新バージョンに向け直します。10 月 8 日には それが 0.5.143 でした。npm はそれをインストールして先に進みました。マシンの中からは 0.5.144 は一度も存在せず、それが基準を超えるころには npm から消えていました。

ロックファイルやエージェントが 0.5.144 を名指しで求めた場合、プロキシは HTTP 451 で ダウンロードを拒否し、npm がそのまま表示する理由を返します。

Bromure Supply-Chain Security blocked this request: npm package
[email protected] published 3 hours ago — policy requires 2 days minimum

エージェントもこの行を読み、自分で一つ前のバージョンに固定します。

同じ Supply Chain パネルには、さらに 2 つの層があります。socket.dev を選んで Block compromised packages をオンにしていれば、socket.dev がパッケージにフラグを立てた 時点でダウンロードは拒否されます。10 月 8 日、Socket がそうするまでにかかったのは 11 分でした。 Strip preinstall / install / postinstall / prepare from npm tarballs on the fly を オンにしていれば、プロキシが tarball を書き換え、"preinstall": "node lib/setup.mjs" の行は npm が目にする前に消えます。その 1 行が、ワームの唯一の侵入口です。

それでも動いたとしたら

すべての層を外して、ペイロードを動かしてみます。ペイロードは VM のユーザーとして、マシン自身 のホームで動き、リストにあるものを探しに行きます。

トークンは身代わりです。 VM 内のどのファイル、環境変数、プロセスにも、本物の GitHub トークン、クラウドのシークレット、SSH 秘密鍵はありません。GH_TOKEN と ~/.git-credentials には正しい形式の ghp_ トークンが入っていて、Mac 上のプロキシは リクエストが GitHub へ出ていくときにだけ本物に差し替えます。~/.ssh に秘密鍵はありません。 SSH_AUTH_SOCK は Mac 上の ssh-agent への橋渡しで、ssh-agent は署名はしても鍵を渡すことは ありません。Mac のホーム、キーチェーン、ブラウザーのプロファイルは、そもそも VM の中にあり ません。ワームが暗号化して送り出すのは身代わりの束で、GitHub も AWS も Kubernetes も、その プロキシを通さない限りどこでも拒否します。

外への出口は Mac を通ります。 Guardrails パネルで Unmatched traffic を Deny にし、モデルプロバイダー、レジストリ、GitHub 向けのルールを置いたマシンは、 iseekaigogo.com にも 30 の Ethereum RPC エンドポイントのどれにも接続しません。設定に かかわらず、こうした接続はすべて、どのマシンでも常に有効な Security Timeline に記録 されます。

書き込みはクリックを待ちます。 新しいマシンは、すべての git フォージで Prompt before write を使います。ワームの GitHub への退避策は、あなたのアカウントに公開リポジトリを作る ことです。これは POST /user/repos、つまり書き込みなので、macOS は Allow write on "github.com" from workspace "…"? を、通信路に現れたままのリクエストと 一緒に表示します。拡散のためにあなたのリポジトリへ .claude/settings.json をコミットして push すれば、同じダイアログが今度は POST /acme/app.git/git-receive-pack とともに出ます。 「Shai-Hulud: Here We Go Again.」という名前のリポジトリを作れとエージェントに頼んだ覚えは ありません。Don't allow をクリックすれば、ワームの拡散はあなたの Mac で止まります。

人質は身代わり

gh-token-monitor ユニットは、それでも VM の ~/.config/systemd/user/ に自分を インストールし、見つけたトークンで 60 秒ごとに api.github.com/user を呼び出し始めます。 そのトークンは身代わりです。プロキシが GitHub 向けに本物に差し替え、GitHub は 200 を返し、 モニターは待ち続けます。

これで対応の順番が逆になります。ノート PC では本物のトークンが攻撃者の手にあり、消去を 引き起こさずに、相手が使う前に失効させる競争になります。Bromure では本物のトークンが VM に 入ったことは一度もないので、攻撃者にはあなたと競う材料がありません。あなたのプロキシの外では、 攻撃者が持ち去った ghp_ の値を受け付けるものは何もないのです。

マシンをシャットダウンすれば、モニターも一緒に止まります。Rewind home… を開き、 インストール前の起動時のチェックポイントを選びます。Bromure は起動に成功するたびに チェックポイントを取っています。ユニットファイル、~/.local/bin のスクリプト、マシンの チェックアウトに仕込まれた .claude/settings.json は、すべて元どおりになります。直前の状態の ホームはまず新しいロールバックポイントとして保存されるので、巻き戻し自体も取り消せます。 まっさらにしたいなら、Reset disk が署名済みのベースイメージからシステムディスクを クローンし直します。

それでもスイッチが作動した場合、rm -rf ~/ が消すのは 1 台の VM の中の /home/ubuntu です。Mac のホームディレクトリには手が届かず、Rewind home… で最後のチェックポイント からマシンのホームを取り戻せます。

Bromure では:バージョンは隠れ、人質は身代わりマシン VM(Ubuntu)$ npm install tensorlake+ [email protected]それでもペイロードが動いたらGH_TOKEN=ghp_…(身代わり)~/.ssh:秘密鍵なしこのマシンのホームだけgh-token-monitor は身代わりでGitHub に問い合わせる消去が届くのはこの VM の /home/ubuntu、Rewind home で元に戻るプロキシ(Mac 上)経過日数ゲート:latest → 0.5.1430.5.144 を名指し → 451、理由を表示ファイアウォール:iseekaigogo.com → 拒否Ethereum RPC エンドポイント → 拒否判定はすべて Security Timeline にmacOS ダイアログ:Allow write on …?POST /user/reposPOST /acme/app.git/git-receive-pack本物のトークンは GitHub 宛てだけに差し替えインターネットregistry.npmjs.orggithub.comモデルプロバイダー攻撃者の C2:一度も接続なしVM の外、Mac に保管本物の GitHub トークン · クラウドキー · SSH 秘密鍵 · あなたのホームフォルダー · チェックポイント · Security Timeline
Bromure のマシンで同じインストールをした場合。経過日数ゲートが 0.5.144 を npm から隠します。それでもペイロードが動けば、盗めるのは身代わりだけで、push やリポジトリ作成は Mac のダイアログを待ち、人質のモニターが握るのは Bromure のプロキシ経由でしか GitHub に受け付けられないトークンです。

誰がインストールしたのか

こうしたインシデントで最初に問うのは、チームのどこかのマシンが問題のバージョンを取り込んだか どうかです。ノート PC なら、ロックファイル、npm のキャッシュ、シェル履歴を探し、ワームが痕跡 を消していないことを祈るしかありません。

Bromure では、サプライチェーンの層が有効かどうかにかかわらず、パッケージの取得はすべて Security Timeline の 1 行になります。npm · [email protected] に、マシン、時刻、判定 が付きます。行は Mac 上に 90 日間保存され、VM の中からは何も編集できません。 Supply chain で絞り込み、バージョンを検索すれば答えが出ます。Export… で CSV に 保存して、インシデントのチケットに添付できます。bromure.io に登録された Mac では、同じ取得が supply_chain.fetch イベントとして組織のイベントストリームにも送られるので、セキュリティ チームは全開発者を対象に一度で問い合わせられます。

問題のバージョンは現れなかった

デフォルトで最低 2 日に設定された経過日数ゲートが、新しすぎるバージョンを npm の バージョン一覧から取り除きます。10 月 8 日、npm install tensorlake は 0.5.143 に 解決されました。

盗まれたトークンは身代わりだった

GitHub、npm、クラウド、SSH の認証情報は Mac に残ります。ワームの人質は、Bromure の プロキシを通したときにしか使えない ghp_ の値でした。

拡散にはあなたのクリックが必要

リポジトリの作成もコミットの push も書き込みです。新しいマシンのデフォルトである Prompt before write では、どちらも VM からは応答できない macOS のダイアログを待ちます。

消去されるのは VM 1 台

rm -rf ~/ が届くのはマシンのホームで、あなたのホームではありません。Rewind home が 前回の起動時に取ったチェックポイントから復元します。

次の定石

Shai-Hulud は今年何度も戻ってきており、その作者たちは、防御側が前回どう対処したかに合わせて 毎回バージョンを作り込んでいます。防御側は数時間でトークンを失効させてきたので、今回は失効を 罰するようにしました。次は別のことを罰するでしょう。クリーンアップスクリプトの実行、 LaunchAgent の削除、プロジェクトを再び開くこと。

ノート PC では、そのどの仕掛けも通用します。あなたが掃除しようとしているまさにそのマシンに、 攻撃者が本物の何かを握っているからです。Bromure のマシンでは、本物の認証情報、あなたの ホームディレクトリ、何が起きたかの記録が、すべてプロキシの向こうの Mac にあります。 掃除は巻き戻しであり、何かをローテーションする前に済ませられます。その背後にある通信路の 境界については、インストールはきれいだった と 脱出 1 回につき 4 万ドル をお読みください。