This document describes Apache Struts2 vulnerability S2-015, affecting versions 2.0.0 to 2.3.14.2. The vulnerability arises when an Action wildcard '*' is configured as a dynamic value, causing OGNL expression execution during parsing. This can lead to remote command execution, though special characters like /, ", and \ are restricted. In Struts 2.3.14.1-2.3.14.2, the setAllowStaticMethodAccess method was removed, preventing direct modification of allowStaticMethodAccess. To execute commands, dynamic methods like (new java.lang.ProcessBuilder('calc')).start() or Java reflection can be used. The document also covers a secondary execution scenario where a Result configuration in struts.xml references an Action variable via ${param}, triggering OGNL expression execution when the variable value is parsed from request parameters.
The vulnerability occurs when an Action wildcard (*) is configured and used as a dynamic value. During resolution, the wildcard content is evaluated as an OGNL expression. For example:
The above configuration allows us to render the page using name.jsp when accessing name.action. However, when the name value is extracted, it undergoes OGNL expression evaluation before being resolved, which leads to command execution. During practical reproduction, it was found that due to the special position of the name value, certain characters such as / " \ cannot be used (escaping does not help either). As a result, some path-dependent commands may fail when exploiting this vulnerability.
Another point worth noting is that in the changelog for Struts 2.3.14.1 - 2.3.14.2, the setAllowStaticMethodAccess method was removed from the SecurityMemberAccess class. Therefore, starting from version 2.3.14.2, it is no longer possible to directly use #_memberAccess['allowStaticMethodAccess']=true to modify its value and regain the ability to invoke static methods.
To achieve command execution, you can use the approach mentioned by researcher kxlzx — calling a dynamic method via (new java.lang.ProcessBuilder('calc')).start(). Alternatively, you can leverage Java's reflection mechanism to indirectly modify it:
Here, the configuration includes <param name="errorMessage">${message}</param>, where message is a private variable in ParamAction. With this configuration, when the Result is triggered, Struts2 retrieves the value of message from the request parameters, and during the resolution process, OGNL expression evaluation is triggered. Therefore, simply submitting %{1111*2} as the variable value will cause the expression to be evaluated. Note that this secondary resolution occurs because ${param} is used in struts.xml to reference a variable from the Action, and it is not specific to the type="httpheader" return method.
S2-015: Remote Code Execution Vulnerability
Affected versions: 2.0.0 - 2.3.14.2
Vulnerability details:
Test environment setup
How it works and testing
The vulnerability occurs when an Action wildcard (*) is configured and used as a dynamic value. During resolution, the wildcard content is evaluated as an OGNL expression. For example:
The above configuration allows us to render the page using name.jsp when accessing name.action. However, when the name value is extracted, it undergoes OGNL expression evaluation before being resolved, which leads to command execution. During practical reproduction, it was found that due to the special position of the name value, certain characters such as / " \ cannot be used (escaping does not help either). As a result, some path-dependent commands may fail when exploiting this vulnerability.
Another point worth noting is that in the changelog for Struts 2.3.14.1 - 2.3.14.2, the setAllowStaticMethodAccess method was removed from the SecurityMemberAccess class. Therefore, starting from version 2.3.14.2, it is no longer possible to directly use #_memberAccess['allowStaticMethodAccess']=true to modify its value and regain the ability to invoke static methods.
To achieve command execution, you can use the approach mentioned by researcher kxlzx — calling a dynamic method via (new java.lang.ProcessBuilder('calc')).start(). Alternatively, you can leverage Java's reflection mechanism to indirectly modify it:
A payload can be constructed as follows:
Direct output:
Beyond the scenario above, S2-015 also involves a case of secondary reference execution:
Here, the configuration includes <param name="errorMessage">${message}</param>, where message is a private variable in ParamAction. With this configuration, when the Result is triggered, Struts2 retrieves the value of message from the request parameters, and during the resolution process, OGNL expression evaluation is triggered. Therefore, simply submitting %{1111*2} as the variable value will cause the expression to be evaluated. Note that this secondary resolution occurs because ${param} is used in struts.xml to reference a variable from the Action, and it is not specific to the type="httpheader" return method.