
本記事はSOCアナリスト 野村和也が執筆したものです
はじめに
SOCでは2025年12月から、日本国内においてReact2Shell(CVE-2025-55182)を悪用したインシデントを多数観測しています。それらの多くはコインマイナーなどが実行されていますが、中には未知のマルウェアが実行されるケースも存在します。
私たちは今回発見した未知のマルウェアをZnDoorと名付け、調査を行いました。ZnDoorは少なくとも2023年12月には使用されていた可能性があり、ネットワーク機器の脆弱性悪用事例と関連があると推察されます。
本稿では、実際に日本国内の企業で観測されたReact2Shell悪用事例と、マルウェアZnDoorの解析結果を共有します。
攻撃フロー
本攻撃の起点となったReact2Shellは、React / Next.jsに存在するリモートコード実行の脆弱性です。既にPoCが公開され、多くのWebサービスが被害を受けており、大きなインパクトを与えています。SOCにおいても、React2Shellを悪用した攻撃を多数観測しています。
ZnDoorが実行される際の攻撃フローは以下のとおりです。

まず、React2Shellの脆弱性が悪用され、以下のコマンドが実行されます。

これによって外部からZnDoorがダウンロード・実行されます。ZnDoorは実行されるとC2サーバと通信を行いますが、そのC2サーバはZnDoorが置かれていた配信サーバと同一でした。
Configデータ
本検体のConfig情報は、`src/structsInfo.InitInfo` 関数に記載されており、Base64にて復号後、AES-CBCで復号が可能です。
Config情報として、以下の文字列が暗号化されて埋め込まれています。
- I6ACRtPz1zjcpsxLoAyGdA==# → api.qtss[.]cc
- DzPe8oPOSSTqDKvfftVsYw==# → 443
また、C2と通信するURL構築を`src/structsInfo.InitInfo` 関数で行います。
- http[:]//api.qtss[.]cc:443/en/about?source=redhat&id=v1.0/en/about?source=redhat&id=v1.1
- http[:]//api.qtss[.]cc:443/en/about?source=redhat&id=v1.1
- http[:]//api.qtss[.]cc:443/en/about?source=redhat&id=v1.21136868377216160297393798828125
定常通信
本検体の関数znF0q.Kは、システム情報をJSON化し、定期的にC2にPOSTします。
キー | 説明 |
|---|---|
ips | ローカルIPをカンマ区切りで列挙した文字列 |
token | ユーザ識別IDをMD5でハッシュ化した16進数文字列 |
user | "v1.3 | <hostname> | <user>"を含む文字列 |
os | "linux | amd64 | pid=<pid>" を含む文字列 |
socket5 | Socks5によるプロキシを使用する際に使用 |
socket5Quick | Socks5によるプロキシを使用する際に使用 |
inListenPor | ポートフォワードを利用する際に使用 |
tokenで使用されるユーザ識別IDは、Golangのxidライブラリを使用して生成されます。これはUUIDと同じくグローバルでユニークなIDとなります。
この JSON 文字列をエンコード後、 src/mode/utils.Post / src/mode/utils.SendDataByPost関数 を使い、 1秒ごとにC2にPOSTしています。
src/mode/utils.Post関数では、User-AgentをSafariに偽装してHTTP通信を行います。


応答が ERROR (hex 4552524f52) であれば 1秒スリープし、最大10回再送します。
コマンド
ZnDoorはRATとしての機能を持ち、C2サーバからコマンド実行が可能です。ビーコンを担当する関数が C2 応答を受け取り、goroutineを起動します。実際のコマンド実行に関する処理は`src/mode/process/ProcessLinux.ProcessTask`に記載されており、コマンドの実行は文字列に応じて分岐します。また、コマンド文字列の分割に、`"##"`を用いています。
ZnDoorに実装されているコマンドを以下に示します。コマンドの実行結果はシリアライズした後、ビーコンと同じ HTTP POST 通信で C2 に送信します。
コマンド | 説明 |
|---|---|
shell | コマンドを実行 |
interactive_shell | 対話型シェルを起動 |
explorer | ディレクトリの一覧を取得 |
explorer_cat | ファイルを読み込み表示 |
explorer_delete | ファイルを削除 |
explorer_upload | C2からファイルダウンロード |
explorer_download | C2へファイル送信 |
system | システム情報を取得 |
change_timefile | ファイルのタイムスタンプを変更 |
socket_quick_startstreams | SOCKS5プロキシを開始 |
socket_quick_stops | システム情報を再構築してGC実行 |
start_in_port_forward | ポートフォワーディングを開始 |
stop_in_port | ポートフォワーディングを停止 |
socket_quick_startsreams コマンドで、SOCKS5プロキシを起動します。
ProcessTask関数においてホスト名やユーザ名、パスワード、ポートなどの情報を取り出し、socket5Quick.StartProxy関数に渡して SOCKS5やクイックプロキシを開始します。
検知回避
ZnDoorでは、以下の検知回避機能を持ちます。
- プロセス名偽装
- タイムスタンプ変更
- 自己再実行
関数znF0q.I は自己再実行を行う関数で、/proc/self/exe を引数付きで起動し、標準入出力を継承したまま子プロセスを動かします。標準入出力を既存のファイルディスクリプタに繋ぎ、親子間で同じ標準入出力を使うことが可能になります。
関数znF0q.Hでは、プロセス名の偽装とタイムスタンプの改ざんが行われます。まず、関数`src/mode/utils.ChangeSelfTime`を呼び、さらにos.Chtimes()を呼ぶことで、自らのプロセスのタイムスタンプを変更します。タイムスタンプは固定で、必ず`2016-01-15 15:08:257450580`となります。さらに、偽装するプロセス名の候補がznF0q.MakeproceNames関数をはじめとした複数の関数で記述されています。デバッガ等でznF0q.MakeproceNames()関数を呼び出すことで、プロセス名のテーブルをデバッガ等で抽出することが可能です。

プロセスの再実行とプロセス名の偽装を繰り返すことにより、PID指定でのプロセスキルや、サンドボックス等による動的解析が困難になります。また、タイムスタンプを過去の日付にすることにより、フォレンジック調査やアンチウイルス検出を回避する目的があると考えられます。
おわりに
本稿では、React2Shell(CVE-2025-55182)を悪用したインシデント事例と、React2ShellによってデプロイされたマルウェアZnDoorについて紹介しました。React2ShellとZnDoorによる攻撃を日本国内の企業においても観測しているため、今後も引き続き注意が必要です。
付録
偽装されるプロセス名の一覧表
プロセス名 |
|---|
/lib/systemd/systemd --user |
/sbin/audispd |
/sbin/auditd -n |
/sbin/multipathd -d -s |
/usr/bin/abrt-dump-oops -xtD |
/usr/bin/lsmd -d |
/usr/bin/pulseaudio --start |
/usr/bin/seapplet |
/usr/lib/accountsservice/accounts-daemon |
/usr/lib/policykit-1/polkitd --no-debug |
/usr/lib/polkit-1/polkitd --no-debug |
/usr/lib/systemd/systemd-journald |
/usr/lib/udisks2/udisksd --no-debug |
/usr/libexec/accounts-daemon |
/usr/libexec/at-spi-bus-launcher |
/usr/libexec/bluetooth/bluetoothd |
/usr/libexec/caribou |
/usr/libexec/colord |
/usr/libexec/evolution-calendar-factory |
/usr/libexec/gconfd-2 |
/usr/libexec/gsd-printer |
/usr/libexec/gvfsd-metadata |
/usr/libexec/ibus-dconf |
/usr/libexec/mission-control-5 |
/usr/libexec/packagekitd |
/usr/libexec/postfix/master -w |
/usr/libexec/rtkit-daemon |
/usr/libexec/tracker-extract |
/usr/libexec/upowerd |
/usr/sbin/abrtd -d -s |
/usr/sbin/chronyd |
/usr/sbin/cron -f |
/usr/sbin/crond -n |
/usr/sbin/cupsd -f |
/usr/sbin/gdm |
/usr/sbin/gssproxy -D |
/usr/sbin/lvmetad -f |
/usr/sbin/ModemManager |
/usr/sbin/NetworkManager --no-daemon |
/usr/sbin/rsyslogd -n |
/usr/sbin/rsyslogd -n -iNONE |
/usr/sbin/sedispatch |
/usr/sbin/smartd -n -q never |
/usr/sbin/sshd -D |
IOC
- 通信先
- api.qtss[.]cc
- 45[.]76.155.14
- 104[.]168.9.49
- 過去のZnDoorの通信先
- 149[.]28.25.254
- 45[.]32.126.137