Nezha監視システム侵入事件インシデント対応実録
実際に発生した40台のVPSへの一斉侵入事件の全記録:脆弱性の発見からバックドアの除去、根本原因の特定、再発対応、自動化による堅牢化までの完全なプロセス。
発生日時:2026-06-19 ~ 2026-06-20 影響範囲:40台のサーバー、Nezha dashboard 2.2.3(管理側)、34台のagentにバックドアが仕掛けられる 事件の性質:Nezha旧バージョンのセキュリティ脆弱性を悪用した持続的バックドアの仕込み
一、事件の背景
私は40台のVPSを複数のクラウド事業者(Tencent Cloud、Alibaba Cloud、Huawei Cloud、RackNerd、NetCup、DMITなど)に分散して保有しており、すべて同一のNezha監視ダッシュボードに接続しています。管理側はTencent Cloudシンガポール(43.156.17.202:8008)にデプロイされており、dashboard v2.2.3を実行しています。
2026-06-19、Nezha公式が複数のセキュリティアドバイザリを公開したことに気付きました。これには旧バージョンにおけるリモートコマンド実行やパストラバーサルによる秘密鍵漏洩などの深刻な脆弱性が含まれていました。確認すべき事項は以下の通りです:
- 私のdashboardと40台のagentは影響を受けるか?
- 攻撃者は既にこれらの脆弱性を悪用して私のサーバーに侵入したか?
- 侵入されていた場合、どのようなバックドアが仕掛けられたのか?どうやって除去するのか?
二、脆弱性分析
影響を受けるバージョン
Nezha公式のセキュリティアドバイザリによると、重要な修正の分岐点は2.2.0です:
- Dashboard/Agent
< 2.2.0:複数の中程度の脆弱性の影響を受ける - それより古いバージョンでは、さらに深刻な脆弱性が重なる
主要なCVE
| GHSA | CVE | 深刻度 | 概要 |
|---|---|---|---|
| GHSA-99gv-2m7h-3hh9 | CVE-2026-46716 | 深刻 | 旧バージョンのRoleMemberがcronインターフェースを介してAgentノードでシェルコマンドを実行可能 |
| GHSA-5c25-7vpj-9mqh | CVE-2026-53519 | 深刻 | 旧バージョンのDashboardで、認証前のパストラバーサルによりjwt_secret_keyが漏洩 |
攻撃チェーン
攻撃者がパネルへのアクセス権限を取得
├─ 方法1:CVE-2026-53519のパストラバーサルを悪用しjwt_secret_keyを漏洩 → 管理者JWTを偽造
├─ 方法2:CVE-2026-46716のcronインターフェースで直接コマンド実行
└─ 方法3:弱いパスワード / 漏洩したパネルアカウント
↓
パネルのリモートコマンド機能(cron / terminal / force-update)を介して
Agentノード上で任意のコマンドを実行
↓
持続的バックドアを仕込む
三、バージョン調査
Dashboardのバージョン
管理側のdashboardはDockerでデプロイされており、バージョン確認は以下の通り:
docker exec nezha-dashboard /dashboard/app -v
# 出力:2.2.3
dashboad 2.2.3 > 2.2.0 であり、アドバイザリの影響を受けません。
Agentのバージョン(40台)
ここで大きな落とし穴がありました:公開WebSocketが返すhost.versionは、ゲストビューではnullであり、実際のバージョンを確認するには管理者JWTが必要です。
最終的に以下の手順で40台のagentバージョンを取得しました:
- 管理側のSQLiteデータベースから
jwt_secret_keyと期限切れでないセッションのkey_idを読み取り - Goとsqids-goを使用して管理者JWTを生成(Nezhaはidcodecでuser_idをsqidsエンコード)
- JWTを使用して管理者WebSocketに接続し、
host.versionを読み取り
結果:
- 管理側agent:2.2.2(最新)
- オンライン中の31台のagent:2.2.2(最新)
- オンライン中の5台のagent:1.9.7(脆弱性の影響を受ける)
- 4台はオフラインでバージョン不明
5台の旧バージョンagent:狗云-MG-V6、hostyun-us、緑雲-日本大版、狗云-MG-V4、racknerd-DC02。
四、第一ラウンドの除去:sysmonバックドア
発見
管理側に対して詳細な読み取り専用チェックを実施したところ、/etc/systemd/system/sysmon.serviceとsysmon-guard.timerを発見。さらに調査を進めると、完全なバックドア体系が明らかになりました:
バックドアの構造:
/usr/local/sysmon/ ← Nezha agentを偽装した偽のバイナリ + config.yml
└─ config.yml ← server: 51.254.44.35:8008(攻撃者のパネル)
/usr/local/.sysmon-guard/ ← デーモン(監視プロセス)
/usr/local/lib/libsysmon.so ← preload注入ライブラリ
/etc/systemd/system/sysmon.service
/etc/systemd/system/sysmon-guard.service
/etc/systemd/system/sysmon-guard.timer ← 30秒ごとに自己復旧をトリガー
/etc/ld.so.preload ← libsysmon.soをロード
バックドアの動作:
sysmonシステム監視サービスを装い、実際にはNezha Agentに類似したバイナリを実行- 設定ファイルの
server:は攻撃者が管理するNezhaパネル51.254.44.35:8008を指す sysmon-guard.timerは30秒ごとにチェックし、sysmonが強制終了された場合は再起動/etc/ld.so.preloadがlibsysmon.soをロードし、グローバルフックを仕掛ける(プロセス/ファイルの隠蔽に使用される可能性)- ログには、デーモンが30秒ごとに
SELF-HEAL: Re-installing cronを実行している記録あり
環境変数で漏洩したバックドア設計:
STEALTH_NAME=sysmon
INSTALL_DIR=/usr/local/sysmon
GUARD_DIR=/usr/local/.sysmon-guard
LIB_PATH=/usr/local/lib/libsysmon.so
PRELOAD_FILE=/etc/ld.so.preload
除去
除去の順序は非常に重要(順序を間違えると監視デーモンがファイルを再作成します):
sysmon-guard.timer、sysmon-guard.service、sysmon.serviceを停止し無効化kill -9で全てのsysmon関連プロセスを強制終了- systemdユニットファイルを削除
/etc/ld.so.preloadからlibsysmon.soの参照を削除/usr/local/lib/libsysmon.soを削除/usr/local/sysmonと/usr/local/.sysmon-guardディレクトリを削除systemctl daemon-reload
除去後の再確認:管理側のsysmonバックドアは完全に削除され、残留プロセス/ファイル/外部接続はありません。
五、40台への一括除去
paramikoスレッドプール(10並行)を使用して、全サーバーに一括で除去を実施。パスワードはメモリ上のみで保持し、ディスクには書き込みません。
除去結果
- 管理側 + 感染した大部分のノード:sysmonバックドアを除去完了
- 3台SSHタイムアウト:raksmart-サンノゼ、雲曦幻境、geelinx-b。手動対応が必要
- Agentバージョンアップグレード:パネルの
force-updateAPI + 手動SSHで、5台の1.9.7を2.2.2にアップグレード
Agentアップグレードの落とし穴
force-updateAPIは、disable_force_update: trueに設定されたagentには無効- 管理側に古いバージョンと表示されていても、必ずしもバイナリが古いわけではなく、プロセスが再起動されていない可能性がある
- 一部のマシンには複数のnezha-agentサービスが存在し、そのうちの1つだけが再起動された
- hostyun-usのIPが45.155.222.112から207.57.137.18に変更されていた
六、再発!真の根本原因を発見
sysmonバックドアの除去後に全量再検査を実施したところ、sysmonバックドアの再発は0でしたが、より危険なものを発見しました。
第二のconfigバックドア
34台のサーバーの/opt/nezha/agent/ディレクトリに、正規のconfig.ymlに加えて、ランダムな名前のconfig-XXXXX.ymlが存在していました:
| フィールド | 悪意のある設定 (config-XXXXX.yml) | 正規の設定 (config.yml) |
|---|---|---|
| server | 207.58.173.192:8088 | data.nat.tf:8008 (=43.156.17.202) |
| disablecommandexecute | false(リモートコマンド有効) | true(既に堅牢化済み) |
| ファイル名 | ランダム5桁のサフィックス | サフィックスなし |
重要な証拠:
data.nat.tfは43.156.17.202(私の管理側)に解決され、config.ymlが正規の設定であることを確認207.58.173.192:8088は現在接続不可だが、設定とプロセスは依然として存在- 管理側で悪意のあるagentプロセス(PID 911、config-etp0h.yml → 207.58.173.192:8088)が実行中だった
- 34台の悪意のある設定ファイル名はすべてランダム5桁のサフィックスであり、自動化された一括仕込みであることを示す
なぜこれがsysmonより危険なのか
| 観点 | sysmonバックドア | 第二のconfigバックドア |
|---|---|---|
| バイナリ | 独立した偽agent (/usr/local/sysmon) | 正規のnezha-agent |
| 設定 | 独立したconfig.yml | config-XXXXX.ymlが正規の設定ディレクトリに混在 |
| 監視 | sysmon-guard.timer 30秒 | systemd nezha-agent-XXX.service |
| 注入 | ld.so.preload | なし |
| 隠蔽性 | 低(独立したパスで明らか) | 高(正規のagentディレクトリに混在) |
| コマンド実行 | C2パネル経由 | 攻撃者のnezhaパネル経由 |
これが真の再発の根本原因です:攻撃者は独立したバイナリを仕込むのではなく、正規のnezha-agentバイナリと追加の設定ファイルを悪用しました。sysmonを除去しても、攻撃者は正規のnezha-agent + 悪意のある設定を通じて、34台のサーバーに対するリモートコマンド実行能力を維持し続けていました。
七、第二ラウンドの除去:第二のconfigバックドア
除去ロジック
# 1. すべてのconfig-*.yml(ランダムサフィックス付きの悪意のある設定)を検出
for cfg in /opt/nezha/agent/config-*.yml; do
# 2. 対応するserviceを検出し停止・無効化
for svc in $(systemctl list-units | grep nezha-agent-); do
svc_cfg=$(systemctl cat "$svc" | grep -oE "/opt/nezha/agent/config[^ ]*\.yml")
if [ "$svc_cfg" = "$cfg" ]; then
systemctl stop "$svc"
systemctl disable "$svc"
rm -f "/etc/systemd/system/$svc"
fi
done
# 3. プロセスを強制終了
pkill -9 -f "$(basename $cfg .yml)"
# 4. 設定ファイルを削除
rm -f "$cfg"
done
systemctl daemon-reload
# 5. 正規のconfig.ymlを堅牢化
sed -i 's/^disable_command_execute:.*/disable_command_execute: true/' /opt/nezha/agent/config.yml
systemctl restart nezha-agent.service
除去結果
- 37台の除去成功:35個の悪意のある設定 + 35個の悪意のあるサービスを削除
- 3台タイムアウト:raksmart-サンノゼ、雲曦幻境、geelinx-b
- 除去に成功した全サーバー:AFTER状態では
config.yml+nezha-agent.serviceのみ。ネットワークチェックはすべてCLEAN
八、詳細な再検査
除去後、40台のサーバーに対して19項目の詳細なIOCスキャンを実施:
- sysmonバックドアファイル
- ld.so.preload注入
- 第二のconfigバックドア
- 正規のconfigの堅牢化状態
- nezhaサービス一覧
- マイニングマルウェアプロセス
- sysmon関連プロセス
- 攻撃者へのネットワーク接続
- cronによる持続化
- systemdの疑わしいサービス
- SSH authorized_keys
- ログイン記録
- SUIDファイル
- /tmpの隠しファイル
- 疑わしいタイマー
- CPU使用率
- 異常なリッスンポート
- rc.local/profile.d
- カーネルモジュール
結果
| 検査項目 | 結果 |
|---|---|
| sysmonバックドアの残留 | 0 |
| 第二のconfigバックドア | 0 |
| マイニングマルウェアプロセス | 0 |
| 攻撃者パネルへの接続 | 0 |
| ld.so.preload libsysmon | 0 |
| 疑わしいcron/systemd | 2台(Tencent Cloud公式のstargate、悪意はないと確認済み) |
| 完全にクリーン | 34/36 |
結論:sysmonバックドアと第二のconfigバックドアのいずれも再発なし。新たなトロイの木馬やハッカーの痕跡は発見されず。
九、自動化による堅牢化
1. リモートコマンド機能の無効化
全34台のnezhaノードのconfig.ymlに以下を設定:
disable_command_execute: true # リモートコマンド実行を無効化(terminal/webssh)
disable_nat: true # NAT透過を無効化
2. Dashboardの自動更新(Watchtower)
Watchtowerコンテナをデプロイし、dashboardの自動更新を実現:
docker run -d \
--name watchtower \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup \
--interval 3600 \
nezha-dashboard
- 毎時、
ghcr.io/nezhahq/nezha:latestに新しいイメージがないか確認 - 新しいバージョンがあれば自動でプルし、
nezha-dashboardコンテナを再起動 - nezha-dashboardのみを監視し、他のコンテナには影響しない
- GitHubで新バージョンがリリースされてから、最大1時間以内に自動更新。手動介入は不要
3. Agentの自動更新
agentの設定を変更:
disable_auto_update: false # 自動更新を有効化
disable_force_update: false # 管理側からの強制更新を許可
disable_command_execute: true # リモートコマンドは無効のまま維持
GitHubで新しいバージョンのagentがリリースされると、各ノードが自動更新。手動介入は不要。
十、再発の根本原因分析
バックドア除去後に再発する場合、通常は以下のいずれかが原因です:
- ★第二のconfigバックドアの除去漏れ(最も隠蔽性が高い):sysmonは除去したが
config-XXXXX.ymlをチェックせず、攻撃者が正規のnezha-agent + 悪意のある設定で制御を維持。これが今回の事件再発の真の根本原因。 - 自己復旧チェーンの残留:sysmon-guard.timer / cron / ld.so.preload / 隠しスクリプトのいずれかが残留
- 攻撃者のログイン経路が依然として存在:rootパスワード、SSHキー、パネルアカウント、Agent Secret、JWT Secretのいずれかが漏洩
- Nezhaのリモートコマンドが適時に無効化されていない:旧バージョンが露出している間に攻撃者がパネルトークンを取得し、コマンドを送信し続けられる
- 同一マシン上の他のパネルの脆弱性:1Panel / 宝塔 / x-ui / MCSManager / phpMyAdmin / MySQL / Redisがパブリックネットワークに露出
- 除去順序の誤り:先にファイルを削除し、監視デーモンを先に停止しなかった → 監視デーモンがファイルを再作成
- パスワードのローテーション未実施:除去後にrootパスワード / SSHキーを変更していない
十一、経験と教訓
1. 一種類のバックドアだけを調べてはいけない
sysmonバックドアは特徴が明確(独立したパス + ld.so.preload)で、発見も除去も容易です。しかし、第二のconfigバックドアは非常に隠蔽性が高い(正規のバイナリ + 設定ディレクトリに混在)。sysmonを除去した後は、必ずconfig-XXXXX.ymlがないか再確認すること。
2. 除去の順序は非常に重要
監視デーモンを停止 → プロセスを強制終了 → ファイルを削除 → ld.so.preloadをクリア → daemon-reload
順序を間違えると、監視デーモンがファイルを再作成し、除去作業が無駄になります。
3. Nezhaバージョン読み取りの落とし穴
公開WebSocketのhost.versionはゲストビューではnullであり、実際のバージョンを確認するには管理者JWTが必要です。これにより、「全agentのバージョン不明」と誤判断し、旧バージョンノードの発見が遅れる可能性があります。
4. Windows SSHの日本語パス
Windows上のOpenSSHは、日本語やスペースを含む秘密鍵パスの処理が信頼できません。ASCIIの一時パスにコピーし、icaclsでACLを厳格化し、システム標準のC:\Windows\System32\OpenSSH\ssh.exeを使用する必要があります。
5. 一括操作にはparamikoを使用
システムにsshpass/expectがない場合、paramikoが一括SSHの最適な選択肢です。パスワードはメモリ上のみで保持し、決してディスクに書き込みません。
6. 自動更新は非常に重要
今回の事件の根本原因は、旧バージョンの脆弱性が悪用されたことです。Watchtowerのデプロイとagentの自動更新を有効化することで、今後GitHubで新バージョンがリリースされれば自動更新され、手動介入が不要になります。
7. セキュリティ堅牢化チェックリスト
- Dashboardを最新版(2.2.3)にアップグレード
- 全Agentを最新版(2.2.2)にアップグレード
- sysmonバックドアを除去
- 第二のconfigバックドアを除去
- リモートコマンド実行を無効化(disablecommandexecute: true)
- Watchtowerをデプロイし、Dashboardの自動更新を実現
- Agentの自動更新を有効化
- rootパスワードをローテーション
- SSHキーをローテーション
- Nezhaパネルのパスワードをローテーション
- Agent Secretをローテーション
- JWT Secretをローテーション
- 不要なパブリックネットワークパネルエントリを閉鎖
十二、オープンソースツール
今回の事件で開発した除去スクリプトをオープンソース化しました:
- リポジトリ:https://github.com/motao123/cleanmalwareandhardennezha
- 完全版除去スクリプト:
clean_malware_and_harden_nezha.sh(3種類のバックドアに対応) - 軽量版:
manual_clean_second_config.sh(第二のconfigバックドアのみ除去)
ワンクリック使用
curl -fsSL https://raw.githubusercontent.com/motao123/clean_malware_and_harden_nezha/main/clean_malware_and_harden_nezha.sh -o /root/clean.sh
bash /root/clean.sh
一括調査コマンド
自分のサーバーが感染していないか確認したい場合:
# sysmonバックドアの調査
ls -ld /usr/local/sysmon /usr/local/.sysmon-guard /usr/local/lib/libsysmon.so /etc/systemd/system/sysmon* /etc/ld.so.preload 2>/dev/null
# 第二のconfigバックドアの調査
ls /opt/nezha/agent/config-*.yml 2>/dev/null && echo "WARNING: second config backdoor detected"
# マイニングマルウェアの調査
ps auxww | grep -Ei "sysmon|kworker|kdevtmpfsi|kinsing|xmrig" | grep -v grep
# 攻撃者への接続調査
ss -tnp | grep -E "51.254.44.35|207.58.173.192"
十三、事件のタイムライン
2026-06-19 攻撃者が旧バージョンの脆弱性を悪用し、sysmonバックドア + 第二のconfigバックドアを仕込む
2026-06-20 00:30 Nezhaのセキュリティアドバイザリを発見し、調査を開始
2026-06-20 00:45 管理側dashboard 2.2.3は影響を受けず、5台のagentが1.9.7であることを確認
2026-06-20 00:51 管理側でsysmonバックドアを発見し、除去を開始
2026-06-20 01:00 sysmonバックドアの除去完了(管理側)
2026-06-20 01:15 40台のsysmonバックドアを一括除去
2026-06-20 01:30 旧バージョンagentを2.2.2に一括アップグレード
2026-06-20 02:00 hostyun-usのIP変更、手動で修正
2026-06-20 10:25 初回再発再検査:sysmonの再発は0だが、34台に第二のconfigバックドアを発見
2026-06-20 11:00 再発の根本原因を特定:第二のconfigバックドア(config-XXXXX.yml → 207.58.173.192:8088)
2026-06-20 11:47 第二のconfigバックドアを一括除去(37台成功)
2026-06-20 11:52 除去スクリプトを更新し、GitHubにプッシュ
2026-06-20 12:00 40台を詳細に再検査(19項目すべて合格)
2026-06-20 12:07 Watchtowerをデプロイ + 自動更新を有効化
2026-06-20 12:13 本ドキュメントを作成
まとめ
今回の事件の核心的な教訓:一種類のバックドアを除去したからといって、すべてのバックドアを除去したことにはならない。sysmonバックドアは特徴が明確で発見されやすいですが、第二のconfigバックドアは正規のバイナリを悪用し設定ディレクトリに混在するため、隠蔽性が非常に高いです。詳細な再検査を実施していなければ、攻撃者は34台のサーバーの正規のnezha-agentを通じて、リモートコマンド実行能力を維持し続けていたでしょう。
セキュリティは継続的なプロセスであり、一度きりの操作ではありません。バックドアの除去は最初のステップに過ぎません。さらに、バージョンアップ、リモート機能の無効化、自動更新の有効化、認証情報のローテーション、不要なパブリックネットワークエントリの閉鎖が必要です。全チェーンを堅牢化して初めて、再び侵入されることを防げます。
本ドキュメントは実際の事件に基づいて整理されています。関連する除去ツールはオープンソース化されています:https://github.com/motao123/cleanmalwareandhardennezha
原文記事アドレス:https://github.com/motao123/cleanmalwareandhardennezha/blob/0772009f3980f8fa3410fe980cd4ab3f3cbd0681/incident-report.md
https://lenghang.com/archives/nezha-jian-kong-xi-tong-ru-qin-shi-jian-ying-ji-xiang-ying-shi-lu