Header background

The anatomy of broken Apache Struts 2: A technical deep dive into CVE-2024-53677

Apache Struts 2 has a history of critical vulnerabilities. The vulnerability allows attackers to manipulate file upload parameters, possibly leading to remote code execution.

A high-level overview of how an attacker can exploit a CVE-2024-53677 vulnerable Struts application to upload a web shell into a web-accessible directory and then remotely execute commands on the web server via the web shell.
Figure 1. A high-level overview of how an attacker can exploit a CVE-2024-53677 vulnerable Struts application to upload a web shell into a web-accessible directory and then remotely execute commands on the web server via the web shell.

While Struts version 6.4.0 introduced a new file upload mechanism ActionFileUploadInterceptor to replace the legacy FileUploadInterceptor, upgrading alone does not mitigate the risk. Applications must migrate to the new mechanism, as using the deprecated file upload mechanism leaves systems vulnerable. Complete mitigation is only guaranteed in Struts version 7.0.0 and later, where the legacy class is fully removed.

This blog post dissects the vulnerability, explains how Struts processes file uploads, details the exploit mechanics, and outlines mitigation strategies. Developers and security professionals should take immediate steps to ensure the security of their Struts-based applications.

Introduction

Apache Struts 2 is a widely used Java framework for web applications, valued for its flexibility and Model-View-Controller (MVC) architecture. However, its history is marked by critical security flaws leading to data breaches.

According to a 2017 article, attackers exploited an unpatched Apache Struts vulnerability (CVE-2017-5638) to expose the sensitive information of over 145 million people. It was reported that the breach resulted from delayed patching, proving how one unaddressed flaw can have detrimental consequences.

The latest vulnerability, CVE-2024-53677, was published in December 2024. It is a critical flaw in the file upload mechanism. This allows attackers to manipulate file upload parameters, leading to unauthorized file placement and potentially remote code execution (RCE).

Implementing file upload with Apache Struts 2

The Struts 2 framework simplifies file uploads by automatically storing uploaded files in a temporary location and passing the file data to the action class. This allows developers to easily access and process the file without handling the upload mechanics directly.

To implement file uploads, the developer would simply need to configure an action mapping, create a form with a file input field, and write an action class that accepts a file. When executed, the action class processes the file by either reading its content or moving it to an uploads folder for later access.

Here’s an example of what the action class should look like.

public class UploadAction extends ActionSupport  {
    private static final String UPLOAD_DIR = "/uploads";
    private File file;
    private String fileFileName;
    private String fileContentType;
    
    public String execute() {
        if (fileFileName == null) return INPUT; // show file upload form page
        try {
            File destFile = new File(UPLOAD_DIR, fileFileName);
            FileUtils.copyFile(file, destFile); // copy file to uploads folder 
            return SUCCESS; // show success page
        } catch (Exception e) {return ERROR;} // file upload failed; show error page
    }
    // constructors, getters, setters, ...
}

In this scenario, the files are stored in the /uploads folder, which is not accessible via the web server.

The corresponding file upload form for the action is as follows:

<s:form action="upload" method="post" enctype="multipart/form-data">
<s:file name="file"/>
<s:submit/>
</s:form>

Notice that the file form field’s name is “file”, and the corresponding attributes in the action class are file, fileFileName, and fileContentType. The file prefix in these attributes corresponds to the form field name.

How does file upload work in Struts?

Before investigating the vulnerability, we need to understand the file upload mechanics and examine some key Struts concepts relevant to it.

Value stack

The value stack simplifies data access between the action, the view, and other components. Unlike a standard stack, it acts as an intermediary for the objects it contains. It also facilitates access to data in the view through OGNL expressions, enabling developers to retrieve stored data efficiently. Consider the following example, where a couple of objects are stored in the value stack.

Figure 2. An example depicting how identically named values for different objects are retrieved from a value stack.
Figure 2. An example depicting how identically named values for different objects are retrieved from a value stack.

When retrieving the value of the expression “x,” the value stack searches for an object with a property named x, starting from the top. The first occurrence is returned, which is the rectangle’s x value in our example. To get the circle’s x value, the indexing syntax [1] can be used to select the second object.

Interceptors

Interceptors are components that process the request before and after an action is executed. They handle tasks such as data validation, logging, and exception handling. In this blog post, we will focus exclusively on the file upload and the parameters interceptors, which are used by default. The ParametersInterceptor assigns HTTP parameters to the corresponding attributes in the action object if it exists. The parameter interceptor uses the OGNL expressions and the value stack to set the value of the action attributes. Likewise, the FileUploadInterceptor class is responsible for preparing the file upload data, by setting the following attributes: the temporary file location, the original file name, and its content type. It relies on the parameters interceptor to set the attributes, by inserting the values into the existing parameters map.

File upload process

The Struts2 file upload process works as follows:

  1. A parameters map is created and populated with non-file HTTP parameters.
  2. The FileUploadInterceptor inserts file details into the parameters map.
  3. The ParametersInterceptor sets action class attributes using the value stack.
  4. The action class is executed, and the file is moved to the /uploads directory.

Understanding the Apache Struts 2 vulnerability

First, let’s examine the example exploit below. A POST request is sent to upload a benign-looking text file named user_file.txt. In our previous file upload example, this file would be uploaded and saved as /uploads/user_file.txt.

However, notice the suspicious path../usr/local/tomcat/webapps/ROOT/shell.jsp in the additional HTTP parameter top.fileFileName. As a result of this path traversal attack, the file will be stored in /usr/local/tomcat/webapps/ROOT/ as shell.jsp instead. This not only makes the uploaded file accessible via the web server but it also makes it executable because of the .jsp extension.

requests.post(
  f"http://localhost:8080/upload.action",
  files={"file": ("user_file.txt", open("user_file.txt").read(), "text/plain")}
  data={"top.fileFileName": "../usr/local/tomcat/webapps/ROOT/shell.jsp"}
)

To understand how the vulnerability works, we need to figure out how the attacker uses the top.fileFileName parameter to alter the file upload location. At first glance, the fileFileName part is the attribute name of our example action class. This attribute should be set by the FileUploadInterceptor. However, an attacker can overwrite it using the top.fileFileName HTTP parameter as part of a mass assignment attack, where they can infer the attribute’s name in the action class. Subsequently, when the action is executed, the temporary uploaded file is copied to a location provided by the attacker.

// UPLOAD_DIR = "/uploads"
// fileFileName = "../usr/local/tomcat/webapps/ROOT/shell.jsp")
File destFile = new File(UPLOAD_DIR, fileFileName);
 
// file = File("TEMP_FILE_LOCATION")
// destFile = File("/usr/local/tomcat/webapss/ROOT/shell.jsp")
FileUtils.copyFile(file, destFile);

So, how does the top. part of the added parameter affects the file upload flow and allows overwriting the file name attribute? Looking at the framework’s documentation, we’re unable to find any reference to such a keyword. However, we do find string references in the source code and a comment in OgnlUtil.java#L503 hinting that top is a special keyword in OGNL.

//special keyword, they must be cutting the stack
if ("top".equals(property)) {
    return root;
}

Another reference to the keyword in CompoundRootAccessor.java#L129 shows that the keyword can be used to retrieve the first element of root, which is an object that holds the elements of the value stack.

if ("top".equals(name)) {
    if (root.size() > 0) {
        return root.get(0);
//... 

We also notice a reference to the top keyword in DefaultExcludedPatternsChecker.java#L39, which is used across different interceptors to check if a given string matches one of the excluded patterns, according to the documentation. This was added to address an earlier vulnerability CVE-2015-5209.

The top is an undocumented keyword intended for exclusive internal use. As mentioned in an earlier version of the security bulletin S2-026, it was also considered for removal.

“Support for expression using top will be dropped in upcoming Struts version 2.5!”

Selecting the first element of the stack is crucial for the exploit to function correctly. This is because, during the execution of an action, the top of the value stack is the action object itself. Additionally, directly using the field name as an expression does not add it to the parameters list, as the same key is used by the FileUploadInterceptor class to insert the file name.

We have seen how the special top keyword allows access to the first element in the value stack. The equivalent indexing syntax ([0]) could also have been used; however, we find that it does not match the accepted patterns defined in DefaultAcceptedPatternsChecker.java#L39.

To summarize, the following illustration demonstrates how the action attributes were set during the request interception before executing the action.

A step-by-step chart on how action attributes are processed before executing the UploadAction.
Figure 3. A step-by-step chart on how action attributes are processed before executing the UploadAction.

For file fields that accept multiple files, the corresponding action attribute should be of type List<String>.

private List fileFileName;

In this scenario, the exploit can be adapted for this type. The indexing syntax ([0]) can be used to select an element from the list. For example, using a parameter named fileFileName[0]. The accepted parameter patterns mentioned above would allow such a name, and the usage of the top keyword is no longer necessary.

Detecting and patching the Apache Struts 2 vulnerability

This vulnerability affects Apache Struts versions from 2.0.0 up to, but not including, 7.0.0. Although the vulnerable FileUploadInterceptor class was deprecated in version 6.4.0 with a vendor recommendation to migrate to the newly introduced ActionFileUploadInterceptor mechanism, the old vulnerable file upload class is completely removed only starting from version 7.0.0. Upgrading to version 6.4.0 is not enough to fully mitigate the risk.

To mitigate this vulnerability, we recommend taking one of the following actions:

  • Upgrade to version 7.0.0 or higher and migrate your file upload actions to use the new ActionFileUploadInterceptor.
  • When upgrading to version 6.4.0 or higher, developers must ensure that all file upload implementations have been refactored to the new ActionFileUploadInterceptor.
  • Exclude certain parameters containing FileName or ContentType patterns from being processed. The following example adds a regular expression to the Parameters interceptor exclusion pattern:
<interceptor-ref name="defaultStack">
<param name="params.excludeParams">^(?:top\.\w+(?:FileName|ContentType))|(?:\w+(?:FileName|ContentType)\[\d+\])$</param>
</interceptor-ref>
  • Modifying the WAF configuration to block requests that include parameters matching the following regular expression:

^(?:top\.\w+(?:FileName|ContentType))|(?:\w+(?:FileName|ContentType)\[\d+\])$.

Runtime Vulnerability Analytics

Organizations still running Apache Struts versions before 7.0.0 are potentially at risk and should discontinue using the legacy file upload mechanism. Dynatrace Runtime Vulnerability Analytics can help detect if the vulnerable method is actively being used within your applications. This allows you to prioritize alerts and strategically address the most critical issues. We’re continuously expanding our vulnerability insights enabling you to stay ahead of potential risks.

Dynatrace Vulnerabilities App showing the vulnerable FileUploadInterceptor actively in use.
Figure 4. Dynatrace Vulnerabilities App showing the vulnerable FileUploadInterceptor actively in use.
Stay tuned as we dive into the details of upcoming vulnerabilities.