技術 約10分で読めます

週間1億DLのaxiosがnpmで乗っ取られ、クロスプラットフォームRATを投下

いけさん目次

2026年3月31日、npmで最も使われているHTTPクライアントライブラリ axios の2バージョン(1.14.1と0.30.4)が侵害された。週間1億ダウンロードを超えるパッケージへの攻撃で、StepSecurityが検出・報告した。攻撃者はメンテナのnpmアカウントを乗っ取り、正規のCI/CDパイプラインを迂回して手動で汚染バージョンを公開。注入された偽の依存関係 plain-crypto-jspostinstall フックでmacOS・Windows・Linuxの3プラットフォームに対応したRAT(Remote Access Trojan、遠隔操作マルウェア)をドロップする。

npmは約3時間後に両バージョンを削除し、plain-crypto-js もセキュリティホールドで置き換えたが、その間にインストールした環境は侵害されたと見なすべきだ。npmサプライチェーン攻撃としてはua-parser-js(2021年)やevent-stream(2018年)と並ぶ規模で、Clinejectionでも取り上げたメンテナアカウント乗っ取りパターンの最新かつ最大級の事例になった。事後の帰属分析でMicrosoftは Sapphire Sleet、GoogleのGTIG(Google Threat Intelligence Group)は UNC1069 として、いずれも北朝鮮系の攻撃グループに断定している。

攻撃タイムライン

時刻 (UTC)イベント
3/30 05:57plain-crypto-js@4.2.0 公開。CryptoJSのソースをコピーした無害なデコイ版。npm上に公開履歴を作り、ゼロヒストリーの検知アラームを回避する布石
3/30 23:59plain-crypto-js@4.2.1 公開。postinstall フックと難読化ドロッパーを追加した攻撃版
3/31 00:21axios@1.14.1 公開。侵害されたメンテナアカウントから手動publish。1.x系ユーザーを標的
3/31 01:00axios@0.30.4 公開。同アカウントから39分後にレガシー0.x系も汚染
3/31 ~03:15npm が両バージョンを unpublish。1.14.1の公開時間は約2時間53分、0.30.4は約2時間15分
3/31 04:26plain-crypto-js がセキュリティホールドスタブに置き換え。マルウェアの公開時間は約4時間27分

Socketの自動マルウェア検出は plain-crypto-js@4.2.1 の公開から6分以内(00:05:41 UTC)に危険判定している。

メンテナアカウント侵害とOIDCバイパス

攻撃者はaxiosのリードメンテナのnpmアカウントを侵害し、登録メールアドレスをProtonMailに書き換えた。ここが技術的に重要なポイントで、axiosの正規リリースはGitHub ActionsからnpmのOIDC Trusted Publisher機構で公開される。OIDC Trusted Publisherとは、GitHub Actionsワークフローとnpmパッケージの紐付けを暗号的に検証する仕組みで、ワークフロー実行時に発行される短命のOIDCトークンでしか公開できない。

axios@1.14.1 のnpmレジストリメタデータにはこのOIDCバインディングがない。つまり攻撃者はGitHub Actionsを経由せず、盗んだ長命のnpmアクセストークンで直接CLIからpublishした。GitHubリポジトリにも1.14.1に対応するコミットやタグは存在しない。npmだけに存在する幽霊リリースだ。

偽依存関係 plain-crypto-js の正体

axios自体には悪意あるコードが1行もない。変更点はたった1つ、package.jsondependenciesplain-crypto-js@^4.2.1 が追加されただけだ。

バージョンdependencies
axios@1.14.0 (正規)follow-redirects, form-data, proxy-from-env
axios@1.14.1 (汚染)follow-redirects, form-data, proxy-from-env, plain-crypto-js@^4.2.1

axiosの全86ファイルを grep しても plain-crypto-jsrequireimport している箇所はゼロ。マニフェストに書かれているが一切使われていない「ファントム依存関係」で、postinstall フックを発火させることだけが目的だ。

plain-crypto-js 自体は正規の crypto-js を偽装している。作者名(Evan Vosberg)、説明文、リポジトリURLまで本物と同じ値をコピーしており、npmページをざっと見ただけでは区別がつかない。

RATドロッパーの難読化と復号

plain-crypto-jspostinstall で実行されるスクリプトは、単一のminified JavaScriptファイルで二層の難読化が施されている。

  1. XOR暗号層。エンコード済み文字列の配列 _e から各エントリを復号する。鍵はJavaScriptの parseInt を通して解析され、有効キーは 1997(文字列中の数字位置6〜9)。各文字はXOR演算 charCode ^ key[i % keyLen] で復元される
  2. Base64 + 文字列反転層。エンコード済み文字列を反転し、= を復元してBase64デコードした後、XOR層に通す

StepSecurityが _e 配列の全エントリを完全復号しており、C2 URLやOSごとのシェルコマンド、ファイルパスなどが平文で回収されている。

攻撃チェーンの全体像

graph TD
    A["npm install axios 1.14.1"] -->|依存関係解決| B["plain-crypto-js 4.2.1<br/>自動インストール"]
    B -->|postinstallフック| C{OS判定}
    C -->|macOS| D["AppleScript生成<br/>tmp配下"]
    C -->|Windows| E["VBScript生成<br/>PowerShellコピー"]
    C -->|Linux| F["curl + Python実行"]
    D -->|osascript実行| G["C2へPOST<br/>npm_mac"]
    E -->|wscript実行| H["C2へPOST<br/>npm_win"]
    F -->|sh -c実行| I["C2へPOST<br/>npm_nix"]
    G --> J["macOS RAT<br/>Library Caches<br/>com.apple.amond"]
    H --> K["Windows RAT<br/>PowerShellスクリプト"]
    I --> L["Linux RAT<br/>tmp .npl"]
    J --> M["自己消去<br/>package.jsonをデコイに差替"]
    K --> M
    L --> M

3プラットフォームとも同じC2エンドポイント sfrclak.com:8000 にPOSTリクエストを送り、POSTボディの npm_mac / npm_win / npm_nix でプラットフォームを区別する。npm_ プレフィックスはSIEM(セキュリティ情報イベント管理)やネットワークログで正規のnpmレジストリ通信に見せかける偽装だ。

OS別のRAT展開

macOS

AppleScriptファイルを /tmp に書き出して osascript で実行する。AppleScriptはC2にPOSTしてmacOS向けRATバイナリをダウンロードし、/Library/Caches/com.apple.amond に保存して実行権限を付与、バックグラウンドで起動する。

パスは意図的に選ばれている。/Library/Caches/ はインシデント対応でまず見ない場所で、com.apple.amond はAppleの逆引きDNS命名規則に似せた偽装名だ。amond は “Activity Monitor Daemon” の略と推測される。実行後、AppleScriptファイルは削除され、永続的に残るのはRATバイナリだけになる。

Windows

3段階で展開される。

  1. where powershell でPowerShellのバイナリパスを特定
  2. PowerShellバイナリを wt.exe(Windows Terminalの名前)としてコピー。正規プロセスに偽装する
  3. VBScriptを生成して wscript で実行。CreateObject("WScript.Shell").Run でウィンドウを完全に隠蔽(vbHide)してC2からPowerShell RATスクリプトをダウンロード・実行

VBScript、ダウンロードしたスクリプトとも実行後に自動削除される。

Linux

最もシンプルで、Node.jsの child_process.exec から直接シェルコマンドを実行する。curl でC2からPythonスクリプトをダウンロードし、/tmp/.npl に保存してバックグラウンドで python3 実行する。

フォレンジック回避の自己消去メカニズム

このマルウェアの厄介な点は、実行後に証拠を消す仕組みが組み込まれていることだ。

  1. postinstall.jsnode_modules/plain-crypto-js/ から削除
  2. package.json を削除
  3. あらかじめ同梱されていた package.json.bak(バージョン4.2.0、postinstall フックなし)を package.json にリネーム

事後に node_modules/plain-crypto-js/ を調べても、クリーンなマニフェストしか見つからない。npm audit を実行しても何も検出されない。ただし、node_modules/plain-crypto-js/ ディレクトリ自体の存在が侵害の証拠になる。正規のaxiosにこの依存関係は存在しないので、このディレクトリがあればドロッパーは実行済みだ。

StepSecurity Harden-Runnerによる実行時検証

StepSecurityはGitHub ActionsランナーにHarden-Runner(カーネルレベルでネットワーク・プロセス・ファイル書き込みを監視するツール)を入れて axios@1.14.1 を実際にインストールし、静的解析の結果を実行時に裏付けた。

検証で確認された2つのC2通信が特に興味深い。

1つ目は npm install ステップ中の01:30:51Z。npm install 開始からわずか1.1秒後にはC2への接続が発生している。依存関係の解決が終わる前にドロッパーが動き出していた。

2つ目は別のワークフローステップ「Verify axios import and version」中の01:31:27Z。npm install は完了済みで、36秒後に全く別のステップでC2コールバックが検出された。ステージ2のPythonペイロード(C2から配信された攻撃コード本体)がバックグラウンドプロセスとして独立動作していた証拠だ。

npmサプライチェーン攻撃の変遷

今回の手口は「メンテナアカウント乗っ取り → 正規パッケージに依存関係注入」というパターンで、2021年のua-parser-js事件と同じ系統に属する。ただし洗練度が段違いだ。

要素ua-parser-js (2021)axios (2026)
悪意コードの配置パッケージ本体に直接埋め込み別パッケージに隔離(ファントム依存関係)
対象OS特定OS向けmacOS, Windows, Linux 全対応
証拠隠滅なしpackage.jsonをデコイに差替え、スクリプト自己削除
事前準備なし18時間前にデコイ版を公開してnpm履歴を偽装
OIDC検証当時は未導入OIDC Trusted Publisher導入済みだが長命トークンで迂回

3月だけ振り返っても、LiteLLMのPyPI汚染(TeamPCPがTrivy経由でCI/CDトークンを窃取)、telnyxのWAVステガノグラフィnpmパッケージでのPastebinステガノグラフィと、パッケージレジストリを狙った攻撃が続いている。攻撃者の手口もPyPI・npm・OpenVSXとエコシステムを横断し、postinstallフック、モジュールインポート時の実行、WAV埋め込みなどトリガー方法を変えてきている。

影響を受けた場合の対応

axios@1.14.1 または axios@0.30.4 をインストールした環境は侵害されたと仮定して動く必要がある。

  1. axios@1.14.0(または0.30.3)にダウングレード
  2. node_modules/plain-crypto-js/ ディレクトリが存在するか確認。あればドロッパー実行済み
  3. macOSなら /Library/Caches/com.apple.amond、Linuxなら /tmp/.npl の存在を確認
  4. npmトークン・SSHキー・クラウド認証情報・CI/CDシークレットをローテーション
  5. npm install --ignore-scripts で再インストールし、意図しないフック実行を防止

npm は両バージョンを削除済みで、plain-crypto-js もセキュリティホールドに置き換わっている。ただし、ロックファイルにバージョンが固定されている環境やプライベートミラーにキャッシュが残っている環境では、引き続き注意が必要だ。

攻撃者の帰属分析

Microsoftは Sapphire Sleet、GoogleのGTIGは UNC1069(2018年以降活動確認)として、本件を北朝鮮系の攻撃グループに帰属させた。npmパッケージを狙ったサプライチェーン攻撃が国家レベルのアクター(攻撃グループ)に公式帰属された事例としては最大規模になる。

北朝鮮系グループがnpmエコシステムを狙う動機は明確で、暗号資産関連のNode.jsプロジェクトが多いことと、開発者の認証情報を窃取すればCI/CDパイプラインへの横展開が可能になることの2点が大きい。今回のRATも、初期侵入の足がかりとしてnpmトークン・SSHキー・クラウド認証情報を収集する設計になっていた。

週間1億DLなのに主要SDKは影響なし

「axiosに依存している有名パッケージが全部踏んだのでは」と思いがちだが、実際にはそうなっていない。

SDKaxios依存HTTPクライアント
firebase-adminなしgaxios (node-fetch)
googleapisなしgaxios (node-fetch)
google-auth-libraryなしgaxios (node-fetch)
@google-cloud/storageなしgaxios + teeny-request (node-fetch)
@anthropic-ai/sdkなし独自実装
openai (v4+)なしfetch API

Google系SDKは数年前から内製の gaxios(axiosライクなAPIだがnode-fetchベース)に全面移行済み。OpenAI SDKもv4でfetchベースに切り替わっている。主要SDKの間接依存でaxiosを踏むパターンは、少なくとも現行バージョンでは発生しない。

では週間1億DLの内訳は何かというと、axiosを直接 npm install しているプロジェクトが大半だ。ブラウザ互換・interceptor・リクエスト/レスポンス変換など、fetchにはない便利機能を理由に使い続けているプロジェクトが膨大にある。Node.js 18以降はネイティブfetchが使えるが、既存コードベースの書き換えコストから移行が進んでいないのが実態だ。

今回の攻撃が刺さるのは、こうした「直接axiosに依存している」プロジェクトで、lockfileにバージョンを固定せず ^1.x のレンジ指定をしていた環境。npm install した瞬間に postinstall が走るため、CIの自動ビルドで無人のまま踏んだケースが最も危険だった。