node-ipc 9.1.6、9.2.3、12.0.1に開発者シークレット窃取バックドア
目次
TL;DR
影響 node-ipc@9.1.6、node-ipc@9.2.3、node-ipc@12.0.1 を2026年5月14日以降に解決した開発端末とCI。npmレジストリの現在の latest は 12.0.0
発火条件 require("node-ipc") のCommonJSエントリポイント読み込み(postinstall 系は未使用)。9.xは読み込みで即実行、12.0.1はハッシュゲート経由で標的絞り込み
対応 9.xは 9.2.1、12.xは 12.0.0 へ戻し、lockfile再生成。該当環境で見えていたnpm、GitHub、SSH、クラウド、Kubernetes、Terraform、DB、AIツール系シークレットは侵害済み扱い
痕跡 node-ipc.cjs 117KB化、$TMPDIR/nt-*、__ntw=1、sh.azurestaticprovider[.]net、37.16.75.69、*.bt.node.js 形式のDNS TXT
node-ipc の3バージョンが2026年5月14日UTCにnpmへ公開され、その数分後にSocketが悪意あるパッケージとして検出した。
対象は 9.1.6、9.2.3、12.0.1。
The Hacker Newsの第一報はSocketとStepSecurityの調査をもとにしていて、どちらの一次情報でも「開発者・CI環境のシークレットを盗む情報窃取バックドア」として扱われている。
攻撃チェーン全体はこの流れになっている。
flowchart TD
A[npm install / lockfile resolve<br/>node-ipc 9.1.6 / 9.2.3 / 12.0.1] --> B[require node-ipc<br/>CommonJSエントリポイント]
B --> C[node-ipc.cjs 末尾の難読化IIFE実行]
C --> D{バージョン}
D -->|9.1.6 / 9.2.3| E[ハッシュゲートなし<br/>そのままfork]
D -->|12.0.1| F[SHA-256ハッシュゲート<br/>module filename一致のみ]
E --> G[子プロセスのデーモン化<br/>__ntw=1 / TMPDIR/nt-PID]
F --> G
G --> H[ホスト探索<br/>SSH / GitHub / Kubernetes / Terraform / クラウド / Keychain]
H --> I[DNSリゾルバをC2 IP 37.16.75.69 へ向け直し]
I --> J[gzipチャンクを *.bt.node.js TXTクエリで送出]
今のnpmメタデータを見ると、node-ipc の latest は 12.0.0 に戻っており、通常のバージョン一覧にも3つの悪意あるバージョンは出てこない。
npmのtimeメタデータには 12.0.1 が2026-05-14 14:25:30 UTC、9.2.3 が14:26:01 UTC、9.1.6 が14:26:25 UTCに公開された記録が残っている。
その時間帯にlockfileや社内レジストリミラーへ取り込まれた環境は、個別に洗う。
発火点はpostinstallではない
npmのインストールスクリプトを追っても発火点には行き当たらない。
StepSecurityとSocketの解析では、悪意のあるコードは node-ipc.cjs の末尾に難読化されたIIFEとして追加されている。
preinstall、install、postinstall は使っていない。
CommonJS利用側で require("node-ipc") が走ると、node-ipc.cjs が読み込まれ、悪意のあるコード本体が動く。
ESM側の node-ipc.js には同じ追加コードはないとSocketは書いているが、アプリ本体、テスト、ビルドツール、古い依存がCommonJSエントリポイントを踏むだけで十分だ。
installは成功したがアプリをまだ起動していない環境と、CIでテストまで走った環境では判断が変わる。
Mini Shai-HuludがTanStackとMistralへ広がった件では、オプション依存とインストールスクリプトが目立つ痕跡だった。
node-ipc ではパッケージインストールログだけに寄せると、実行済みの環境を見落とす。
9.xは広く動き、12.0.1はハッシュゲートで絞る
3バージョンは同じ名前のパッケージでも、実行条件が同じではない。
StepSecurityは、12.0.1にだけSHA-256のハッシュゲートがあり、対象のmodule filenameに一致しない環境では全面的な置き換えまでは進まない、と説明している。
9.x側にはこのゲートがなく、読み込まれた環境で悪意のあるコード本体がそのまま動く。
| 項目 | 9.1.6 / 9.2.3 | 12.0.1 |
|---|---|---|
| ハッシュゲート | なし | あり(module filenameのSHA-256) |
| require時の挙動 | 即fork、デーモン起動 | ゲート一致時のみfork。不一致は __ntRun エクスポート追加で終了 |
| 影響範囲 | 読み込んだ環境すべて | 攻撃者が事前に決めた標的環境のみ |
| 元のnode-ipc API | 通常通り動く | 通常通り動く |
| 戻し先 | 9.2.1 | 12.0.0 |
ハッシュゲートの中身は、require から渡される module filename のSHA-256を取り、攻撃者が埋め込んだ固定値と比較する。
特定の開発者・特定のCIランナーで node-ipc がロードされたときだけ起動する形になっていて、無関係な環境では __ntRun を追加する以外何も起きないように見える。
このため、12.0.1を入れてアプリが普通に動いていた環境でも、ハッシュゲート対象だった可能性が残り、追跡対象から外せない。
この差分で、影響調査の優先度が変わる。
9.1.6 または 9.2.3 がlockfile、node_modules、CIキャッシュ、社内ミラーに残っていたら、読み込み済みかどうかを厳しく追う。
12.0.1 はハッシュゲートの対象外なら本体APIを壊さずに __ntRun エクスポートを追加する挙動が確認されているため、アプリが普通に動いていた環境も追跡対象に含める。
悪意のあるコード本体は子プロセスをforkし、__ntw=1 という環境変数でデーモン側の実行経路を分ける。
$TMPDIR/nt-<PID>/ 形式の作業ディレクトリも使う。
開発端末では ps auxeww | grep __ntw、Linux CI ランナーでは /proc/*/environ の __ntw、一時ディレクトリの nt-* を確認する。
盗む対象は開発端末・CIで触れる認証情報全般
収集対象はnpmトークンだけではない。
Socketの解析では、環境変数、SSH鍵、GitHub CLI設定、GitLab CLI、npm、Yarn、Netrc、Terraform、Kubernetes、Docker、Helm、Rancher、クラウド設定、DB設定、シェル履歴、macOS Keychain、Linux keyring、KWallet、FileZillaやOpenVPNの接続情報まで含まれる。
StepSecurity側も、Claude AIやKiro IDE設定を含む開発者向けの広い秘密情報を挙げている。
ここはPCPJack Credential Stealerがクラウド認証情報を盗む件と近い。
入口はnpmパッケージだが、盗まれるのは端末やランナーが普段の作業で触れる認証情報そのものだ。
.env、クラウド設定、Kubernetesトークン、Terraform state、DBパスワードを同じ環境に置いていた場合、node-ipc を消したあとに、その環境で触れていた認証情報の洗い出しとローテーションが続く。
DNSログだけではbt.node.jsが見つからない
ネットワーク側では sh.azurestaticprovider[.]net が目立つ。
Microsoftの正規 azurestaticapps.net に似せた名前だが、別ドメインだ。
StepSecurityの解析時点では 37.16.75.69 に解決され、C2(攻撃者の指令サーバー)として使われていた。
もう一段の仕掛けがDNS TXT経由のデータ窃取だ。
悪意のあるコード本体は 1.1.1.1 または 8.8.8.8 でC2ドメインを解決したあと、リゾルバをC2 IPへ直接向け直す。
そのうえで、gzipアーカイブのチャンクをTXTクエリラベルとして送る。
Socketは xh、xd、xf プレフィクスを持つ *.bt.node.js 形式の大量TXTクエリを検知機会として挙げている。
DNS TXTを選ぶ理由は構造上はっきりしている。
企業の外向き通信ファイアウォールはHTTP・HTTPSをログ取得・遮断の対象として扱うが、UDP/53は通常業務で塞ぎにくく、監視も薄くなりがちだ。
C2側の権威DNSサーバーを攻撃者が握っていれば、TXTクエリのラベル部分がそのまま片方向のデータチャンネルになる。
リゾルバを 1.1.1.1 / 8.8.8.8 から 37.16.75.69 へ切り替えるのは、企業DNSリゾルバの中継を抜いて攻撃者側の権威DNSサーバーへ直接投げるためで、これによって組織側のDNS観測ログから痕跡が消える。
この bt.node.js は通常のpublic DNSに出るとは限らない。
StepSecurityは、C2がDNSの受け口として直接受けるため、企業のDNSリゾルバログだけに依存している組織では拾えないと書いている。
アプリケーションプロセスから 37.16.75.69 へのUDP/53、sh.azurestaticprovider.net または 37.16.75.69:443 への外向き通信、短時間に増えるTXTクエリ量をログ側で切り出す。
lockfileとcacheに残った3バージョンを切り出す
StepSecurityが挙げている確認はかなり直接的だ。
依存ツリーとlockfileで3バージョンを探す。
npm ls node-ipc
npm ls node-ipc --all 2>/dev/null | grep -E '9\.1\.6|9\.2\.3|12\.0\.1'
grep -E '"node-ipc".*"(9\.1\.6|9\.2\.3|12\.0\.1)"' package-lock.json
grep -E 'node-ipc@(9\.1\.6|9\.2\.3|12\.0\.1)' yarn.lock
grep -E 'node-ipc.*9\.1\.6|9\.2\.3|12\.0\.1' pnpm-lock.yaml
pnpmのgrepはlockfileの書式によって拾い方が雑になるので、ヒットしない場合でも node-ipc のエントリを目視する。
CIキャッシュ、社内npmプロキシ、Verdaccio、Artifactory、Docker image layerにtarballが残っている場合もある。
npmレジストリ側で見えなくなっていても、組織内のキャッシュは別の時計で動く。
StepSecurityは既知のクリーン版として、9.x利用者は 9.2.1、12.x利用者は 12.0.0 へ戻す手順を出している。
該当バージョンを読み込んだ端末やランナーでは、バージョンを戻す前にその環境で見えていたシークレットを棚卸し、ローテーションする。
node-ipc.cjs のSHA-256 96097e0612d9575cb133021017fb1a5c68a03b60f9f3d24ebdc0e628d9034144、tarballハッシュ、node-ipc.cjs の117KB化も調査材料になる。
node-ipcは2022年にも問題を起こしていた
node-ipc は2022年にも悪意のあるコードで大きく燃えている。
当時はロシア・ベラルーシにあるシステムのファイルを上書きする抗議目的の破壊コードと、peacenotwar 依存パッケージが問題になった。
Socketの表現では、今回の3バージョンは既知パッケージへの疑わしい再公開、または悪意のあるコードの再導入に近い。typosquatting(名前を似せた偽パッケージの手口)とは別の経路だ。
今回使われたnpmアカウント atiertant は、Socketによれば node-ipc のメンテナ一覧にいるが、このパッケージでの直近publish履歴はなかった。
StepSecurityは、期限切れドメインからnpmアカウント復旧用メールを乗っ取った説にも触れている。
確定した侵害経路として読むにはまだ材料が足りないが、休眠メンテナが残った人気パッケージは、それ自体が侵入口になる。
最近のnpm被害は、RubyGemsの新規登録停止記事で書いたような大量スパム型とも、Mini Shai-HuludのようなCI/OIDC悪用型とも違う形で続いている。