Struts 2 Security Vulnerability - Dynamic Method Invocation

Introduction

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 2.2.1.1 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 2.3.1.1 in December 2011, the security issue with using dynamic method invocation was addressed. However, by default even in version 2.3.1.1, 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:

http://localhost:8080/mywebapp/recoverpassword!getUserPassword.action

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/2.2.1.1/docs/action-configuration.html#ActionConfiguration-DynamicMethodInvocation.

Example Application

To help illustrate the vulnerability, I've created a simple example application which you can download or view here:

http://www.bpjava.net/struts2_security_vulnerability/

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:

http://localhost:8080/struts2_security_vulnerability/index.action
(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:

http://localhost:8080/struts2_security_vulnerability/recoverpassword!getPassword.action

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:

http://localhost:8080/struts2_security_vulnerability/recoverpassword!getPassword.action

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:

http://localhost:8080/struts2_security_vulnerability/changepassword!changePassword.action?newPassword=my_new_password&username=bruce

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:



<init-param>
<param-name>struts.enable.DynamicMethodInvocation</param-name>
<param-value>false</param-value>
</init-param>

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

http://localhost:8080/struts2_security_vulnerability/recoverpassword!getPassword.action

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.

Summary

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.

Related Blog Entries

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
I wouldn't call this a security vulnerability. It takes work to write the bad code that might cause a security problem, not a deficiency with struts2 itself. In the end, this issue is user error.
# Posted By Jesse | 7/8/11 6:04 PM
Jesse - appreciate your comment.

Users don't have to do any work to enable the security vulnerability -- it's in Struts 2 by default -- the ! operator is enabled. Users must do extra work to turn it off. Since most users new to Struts 2 probably are not aware of the ! operator they don't know how to turn it off and reduce this security vulnerability.
# Posted By Bruce | 7/9/11 6:01 AM
Actually, it's better to use the functionality added in 2.3: set the attribute strict-method-invocation="true" on your package. Then specify allowed-methods as a comma-separated list of method names in your action. This gives all the flexibility of DMI and the security of knowing that only the methods you allow can be called.
# Posted By John Lindal | 12/26/11 2:13 PM
Does the same security problem exists with Wildcard Method Selection?
# Posted By Andi | 1/3/12 12:31 PM
Andi:

I tested using the wildcard method selection and the same vulnerability does exist. However, a Struts 2 programmer must explicitly enable wildcard method selection in his struts.xml otherwise the wildcard method doesn't work and the vulnerability does not exist.

Dynamic method invocation is enabled by default and the struts 2 developer must turn it off through a property setting.

Either way it's very important to review your action class public methods to ensure you have not exposed anything that should not be accessible to the user.
# Posted By Bruce | 1/3/12 2:38 PM
@Bruce:
i excpected that. i'm just wondering why strict-method-invocation is only implemented for DMI and not for wildcard method invocation (at least it's only documented for DMI).
I think i should ask this on the struts mailing list.
# Posted By Andi | 1/3/12 5:24 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1.002. Contact Blog Owner