概要
最も人気のあるJavaScript HTTPクライアントライブラリであり、週間1億以上のダウンロード数を誇るaxiosの悪意あるバージョン2つがnpmに公開されました:axios@1.14.1とaxios@0.30.4。偽の依存パッケージplain-crypto-js@4.2.1がランタイム依存として注入されました。このパッケージにはpost-installスクリプトが含まれており、npm installから2秒以内にリモートアクセス型トロイの木馬(RAT)を展開しました。npmが残りの依存関係の解決を終える前に、マルウェアはすでに攻撃者のC2サーバーに通信を開始していました。すべてのアーティファクトは実行後に自己破壊するよう設計されており、フォレンジックの痕跡を最小限に抑えています。
攻撃の手法
段階1 ― メンテナーアカウントの乗っ取り
axiosメンテナーのnpmアカウントが乗っ取られました。感染したパッケージはこのアカウントからnpmレジストリに直接プッシュされ、GitHubの通常のCI/CDパイプラインを完全に迂回しました。特筆すべきは、バージョン1.14.1はGitHub上に一切存在しないことです。侵害されたnpmアカウントを通じてのみ公開されたため、GitHubリポジトリの変更を監視している人からは見えませんでした。
段階2 ― 偽の依存パッケージのステージング(18時間前)
悪意あるaxiosバージョンの公開前に、攻撃者は偽のパッケージplain-crypto-js@4.2.1をステージングしました。このパッケージは匿名のProton Mailアカウントから公開されました。
名前は正当に見えるよう意図的に選ばれました。HTTPライブラリはTLS、リクエスト署名、レスポンスのデコードのために暗号パッケージに依存するのが一般的です。偽のパッケージは正規のcrypto-jsパッケージと同じ説明文とウェブサイト参照を流用しており、一見しただけでは区別が困難でした。
タイミングも意図的でした:plain-crypto-jsは感染版axiosの18時間前に公開されました。両方を同時に公開すると疑念を招くため、時間差を置くことで依存パッケージが独立して存在しているように見せかけました。
偽の依存パッケージには以下が含まれていました:
- axiosのインストール時に即座にRATを起動する
postinstallスクリプト - クリーンな
package.jsonのマークダウン版を生成し、実行後に感染版のpackage.jsonをクリーン版に置き換えるメカニズム — 自らの痕跡を消去
段階3 ― axiosへの偽依存パッケージの注入
39分以内に、2つのaxiosバージョンが公開されました:
-
axios@1.14.1(最新の1.xブランチのユーザーを標的) -
axios@0.30.4(レガシーの0.xブランチのユーザーを標的)
両バージョンは正規の前バージョンと同一で、唯一の追加点はランタイム依存としてのplain-crypto-js: "^4.2.1"でした。このパッケージはaxiosのソースコード内で一切インポートも呼び出しもされません。postinstallスクリプトを発火させてRATを展開するための手段としてのみ存在していました。
段階4 ― ペイロードの実行
plain-crypto-jsのpostinstallスクリプトはnpm installの際に即座に実行されます。3つのOS向けペイロードが用意されていました:
- Linux
- macOS
- Windows
インストールから2秒以内に、マルウェアは攻撃者のC2サーバー(http://sfrclak.com:8000/)への通信を開始しました。npmが残りの依存関係の解決を終える前のことです。実行後、ペイロードは自己破壊し、感染したpackage.jsonをクリーン版に置き換えて痕跡を隠蔽します。
この攻撃の検知が困難だった理由
-
axios自体のコード変更なし: axiosのソースコードは一切変更されていません。
package.jsonに依存パッケージが1つ追加されただけでした。 -
依存パッケージが正当に見えた:
plain-crypto-jsは説得力のある名前、一致する説明文を持ち、事前に公開されていました。 - CI/CDの迂回: バージョンはnpmに直接公開され、GitHubリポジトリには一切現れませんでした。
- 自己破壊するアーティファクト: マルウェアは実行後に自らを消去し、事後分析をより困難にしました。
- 2つのブランチを同時に汚染: モダン(1.x)とレガシー(0.x)の両ブランチが標的となり、影響範囲を最大化しました。
侵害の確認方法
インストール済みバージョンの確認
npm ls axios
axios@1.14.1またはaxios@0.30.4が表示された場合、影響を受けています。
偽の依存パッケージの確認
npm ls plain-crypto-js
結果が返された場合、悪意ある依存パッケージがnode_modules内に存在しています。
ネットワークログの確認
侵害期間中にnpm installを実行したシステムからsfrclak.com:8000への外向き接続がないか確認してください。
残留アーティファクトの確認
マルウェアは自己破壊しますが、クリーンアップが不完全な場合、痕跡が残ることがあります。ロックファイル内のplain-crypto-jsへの参照を検索してください:
grep -r "plain-crypto-js" package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null
対処法
-
侵害されたバージョンのアンインストール:
npm uninstall axios npm install axios@1.14.0 # または最新の安全が確認されたバージョン -
偽の依存パッケージの削除:
npm uninstall plain-crypto-js -
影響を受けたマシン上のすべてのシークレットをローテーション: RATが実行された環境からアクセス可能な認証情報、トークン、キーはすべて侵害されたとみなし、即座にローテーションしてください。
-
システムの永続的アクセスを監査: RATが初期ペイロード以外の永続化を確立している可能性があります。以下を確認してください:
- 予期しないcronジョブやスケジュールタスク
- 不明なプロセスやサービス
- 不正なSSHキー
-
ロックファイルの更新:
package-lock.json/yarn.lock/pnpm-lock.yamlをクリーンな状態から再生成し、plain-crypto-jsへの参照が残っていないことを確認してください。
防御策
- ロックファイルの使用と依存関係変更のレビュー: パッチバージョンに新しいランタイム依存が追加されるのは警告サインです。ロックファイルにより、コードレビュー時にこれが可視化されます。
-
postinstallスクリプトの無効化: インストール時に
--ignore-scriptsの使用を検討するか、allow-scriptsなどのツールでライフサイクルスクリプトを実行できるパッケージをホワイトリスト化してください。 - npmへの直接パブリッシュを監視: パッケージバージョンがnpmに存在するが対応するGitHubリポジトリに存在しない場合、疑わしいものとして扱ってください。
- パッケージレジストリで2FAを有効化: メンテナーアカウントの乗っ取りは依然として最も一般的な侵入経路です。npm、PyPI、その他のレジストリでの2FA必須化は不可欠です。
-
正確な依存バージョンの固定: レンジ指定(
^、~)ではなく正確なバージョンを使用し、侵害されたバージョンへの自動解決を防いでください。