The Struts 2 web application framework has a long-standing security vulnerability that may not be well known to new Struts 2 developers. By default the framework enables a technique called dynamic method invocation. This technique allows a developer to specify in a Struts 2 action url what method should be called in the Action class. The security problem is that any user of the Struts 2 web application can also use dynamic method invocation to call a public method that is in the Action class. The purpose of this article is to explain the vulnerability and how to fix it. Note this problem occurs through the Struts 18.104.22.168 release, which is the latest release as of February 2011.
Please note that this security issue is well-known to most experienced Struts 2 developers and to the Struts 2 framework committers. They are working on a permanent fix in a future Struts 2 release. You can learn more about this vulnerability and its fix by searching the Struts 2 developers mailing list. My goal with this article is to better inform new Struts 2 developers about this vulnerability and how to prevent it.
NOTE: With the release of Struts 22.214.171.124 in December 2011, the security issue with using dynamic method invocation was addressed. However, by default even in version 126.96.36.199, the vulnerability exists and developers must add additional markup to their package statement to prevent users being able to execute public methods. See http://struts.apache.org/2.3.1/docs/action-configuration.html#ActionConfiguration-DynamicMethodInvocation for more detail.
Dynamic Method Invocation
In Struts 2, you can use the bang (!) operator to specify which method should be executed in the Action class. For example, let's say I have an action named recoverpassword that is mapped to class RecoverPassword. In class RecoverPassword is public method getUserPassword. I can use the bang operator to tell the Struts 2 framework to execute method getUserPassword when action recoverpassword is specified in the URL. I can do this by constructing the URL as follows:
The part after the bang operator is the method Struts 2 should execute in the class associated with the recoverpassword action.
Note that method getUserPassword must be public and have no parameters. In Struts 2, by default, dynamic method invocation is allowed. Any user of the web application can play around with calling your application's URLs trying to get Struts 2 to execute a method.
For more information about dynamic method invocation see: http://struts.apache.org/188.8.131.52/docs/action-configuration.html#ActionConfiguration-DynamicMethodInvocation.
To help illustrate the vulnerability, I've created a simple example application which you can download or view here:
You can download the example project, named Struts2_Security_Vulnerability_Example. The project was created using Eclipse 3.6 and Maven. It is a standard Maven project so if you don't have Eclipse, you should be able to import the unzipped project into any Java IDE that supports Maven.
To build the application's war file run mvn clean package from the project's root folder. The war file is created in the target sub-folder. Copy the war file to your Servlet container (e.g. Tomcat, GlassFish) and then startup the Servlet container.
In a web browser go to:
(note for this URL and the others that follow if you want to use the example application I deployed to my own server, replace localhost:8080 in the URL with www.bpjava.net - for example http://www.bpjava.net/struts2_security_vulnerability/index.action).
You should see a web page with Welcome to Struts 2!
Security Vulnerability Caused By Dynamic Method Invocation
The Action class RecoverPassword has a public method called getPassword and the project has not disabled dynamic method invocation. So a user entering this URL in the browser:
will see the String returned by the method getPassword. Note that recoverpassword must be matched to an Action class that has a public getPassword method that has no parameters.
The result of the url:
will either be a 404 error or a stack trace if the struts devmode property is true. The 404 error page will include this line:
No result defined for action edu.ku.it.si.struts2securityvulnerability.security.action.RecoverPassword and result user_secrect_password
The last part above is the String returned by method getPassword, which in this example is the user's password. The stack trace will include a similar line that exposes the user's password.
Any public method in the Action class that has no parameters is vulnerable, even if the method returns no value. For example enter this URL in the example application:
You will not see anything in the browser after the URL executes, but the above URL will cause method changePassword to be executed (see class ChangePassword ) and the password for user bruce will be changed to my_new_password. This URL completely bypasses the authentication check in the execute method of class ChangePassword. (To verify that method changePassword was called see the console output after executing the above URL.)
How To Fix The Dynamic Method Invocation Security Vulnerability
Include in struts.xml this Struts 2 property setting:
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
or in struts.properties:
struts.enable.DynamicMethodInvocation = false
or in web.xml include this init-param node in the Struts 2 filter:
In the example application's struts.xml uncomment that property setting to fix the problem in the example.
The above setting will prevent Struts 2 from parsing the bang operator (!) in the URL, so then the whole part before .action will be used to match to a configured Struts 2 action.
For example with this URL
instead of method getPassword being called on the Action class matched to action recoverpassword, the Struts 2 framework will try to find an action named "recoverpassword!getPassword" (which won't exist).
Other Good Practices To Follow When Designing Struts 2 Action classes
1. The only methods in the Action class that should be public are the those methods specifically matched to a Struts 2 action (e.g. execute, input) and get/set methods for the instance fields that are exposed to the view pages.
2. All methods that do some work on behalf of the action should be in a Service layer class and not in the ActionSupport class.
Hopefully this article will convince Struts 2 developers to always include setting the DynamicMethodInvocation property to false to prevent the use of the bang operator to call specific methods in the Action class.