On 22 August 2018, a Semmle security researcher disclosed a critical vulnerability affecting the versions 2.3
to 2.3.34
and 2.5
to 2.5.16
of Apache Struts 2, one of the most used Java-based web application frameworks.
This unauthenticated Remote Code Execution vulnerability is easily triggered by visiting a specially crafted URL which allows the attacker to fully control the code executed on the server. This exploit requires a specific configuration that is not present on all Struts 2 applications, however, it does exist in commonly seen configurations for some Struts plugins.
According to Semmle, two requirements should be satisfied by an application to be vulnerable:
alwaysSelectFullNamespace
flag must be set to true in the Struts configuration.Before discussing how you can identify and exploit this flaw, let's have a look at the technologies which accidentally produced the vulnerability. This will help you understand the vulnerabilities root cause.
Apache Struts 2 is a free, open-source framework providing an Model-View-Controller (MVC) architecture for web application development. RedMonk analyst Fintan Ryan stated that in 2017 at least 65 percent of the Fortune 100 companies used Apache Struts 2 as a framework for their web applications.
Apache Struts 2 initial release announced several important features and technologies that were added to the framework, making it considerably more efficient compared to the previous version. One of these features is the Object Graph Notation Language (OGNL), a template engine whose purpose is to create the views by transforming input parameters into OGNL statements and run them on a model.
As OGNL is very powerful, it quickly became a point of interest for attackers. A significant percentage of Struts 2 vulnerabilities occurred due to this technology, allowing attackers to execute malicious OGNL statements.
The vulnerability occurs because Struts insufficiently validates the user specified value of a namespace
in the core of the framework.
A namespace is a concept used by the Struts 2 framework to group actions into logical modules so that two or more actions with the same name can exist in different namespaces.
Consequently, each namespace can have its own "menu" or "help" action, each with its own implementation (Read more in the Struts 2 Developer Guide).
An example is:
<action name="/editUser" class="org.apache.struts.webapp.example.EditUserAction">
<result>User.jsp</result>
</action>
<action name="/editSubscription" class="org.apache.struts.webapp.example.EditSubscriptionAction">
<result>Subscription.jsp</result>
</action>
<action name="/editRegistration" class="org.apache.struts.webapp.example.EditRegistrationAction">
<result>Registration.jsp</result>
</action>
Instead of hard coding the configuration file, Java developers can use some patterns to achieve the same result.
For example using Wildcard Mappings, the above configuration can be re-factored to only three lines of code:
<action name="/edit*" class="org.apache.struts.webapp.example.Edit{1}Action">
<result>{1}.jsp</result>
</action>
The redirectAction
result type uses ActionMapper
interface to redirect the browser to a URL that invokes the specified action and (optional) namespace.
The best way to explain how Struts 2 ActionMapper
interface works for the above configuration is to show a simplified workflow diagram for two cases. Let's suppose you make two simple GET
requests.
Since the help action does not have a namespace parameter specified in struts.xml
and the alwaysSelectFullNamespace
flag is set to true, Struts uses everything between the domain name and the last slash as a namespace. After that, it calls the translateVariables
function to check whether the namespace is a string or an OGNL expression. In case the user has provided an ONGL expression, the doExecute
method executes it and returns the result. That is where a user can submit code for execution.
Apache quickly implemented a patch for this vulnerability by adding the cleanNamespaceName
method whose purpose is to check whether a namespace is valid or not based on a allowlist approach.