Protect your Struts1 applications

Since the last post on how to mitigate the Struts 2 zero day on the wild we have received many queries from customers wondering if their legacy Struts 1 applications were also vulnerable to this same attack.

 

The Struts security team confirmed that classloader manipulation is also possible in Struts 1 applications even if they don’t use OGNL for parameter binding. They have assigned CVE-2014-0114 to this issue.

Further discussions with Struts security team have confirmed that although classloader manipulation has been verified, remote code execution has not been confirmed yet.

At HP we don’t wait for an exploited zero day before we take action to protect our customers. Therefore, while the Struts team works on a patch for this issue, we strongly recommend Struts1 developers to take the following actions to mitigate the risk.

Struts1 lacks the Struts2 Interceptor chain so we cannot benefit from the Parameter Interceptor to do the work for us. But we can easily build our own using Servlet Filters for the Struts’s org.apache.struts.action.ActionServlet Servlet.

Struts ActionForm bean population relies on ServletRequest.getParameterNames() to first get a list of all parameters being sent on the query and proceed to set those parameter on the ActionForm bean properties.

The following Filter will create a ServletRequest wrapper that overwrites getParameterNames() method and checks the parameter names against a customizable regular expression. If a parameter name matches this regular expression, it will be removed from the list returned by ServletRequest.getParameterNames().

Figure 1: ParamFilter.java

 

package com.company.filters;

import java.io.IOException;
import java.util.*;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public final class ParamFilter implements Filter {

    FilterConfig filterConfig = null;

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String regex = this.filterConfig.getInitParameter("excludeParams");
        chain.doFilter(new ParamFilteredRequest(request, regex), response);
    }

    public void destroy() { }
    
    static class ParamFilteredRequest extends HttpServletRequestWrapper {

        private HttpServletRequest originalRequest;
        private String regex;

        public ParamFilteredRequest(ServletRequest request, String regex) {
            super((HttpServletRequest)request);
            this.originalRequest = (HttpServletRequest) request;
            this.regex = regex;
        }
        public Enumeration getParameterNames() {
            List<String> requestParameterNames = Collections.list((Enumeration<String>) super.getParameterNames());
            List finalParameterNames = new ArrayList();

            for (String parameterName:requestParameterNames) {
                if (!parameterName.matches(regex)) {
                    finalParameterNames.add(parameterName);
                    System.out.println("Param : " + parameterName);
                }
            }
            return Collections.enumeration(finalParameterNames);
        }
    }
}

 

Figure 2: Web.xml configuration

 

    <filter>
        <filter-name>ParamFilter</filter-name>
        <filter-class>com.company.filters.ParamFilter</filter-class>
        <init-param>
            <param-name>excludeParams</param-name>
            <param-value>(.*\.|^|.*|\[('|"))(c|C)lass(\.|('|")]|\[).*</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>ParamFilter</filter-name>
        <servlet-name>YOUR ACTION SERVLET</servlet-name>
    </filter-mapping>

 

We would like to thank the Struts security team for their prompt response and their validation of the proposed mitigation filter.

 

Update (01/05/2014):

 

HP Security Research Team has confirmed remote code execution is possible on certain application servers and has notified Struts security team.

 

Stay Secure!

Tags: 0day| Struts1
Labels: 0day| Struts1
Comments
Arturo T(anon) | ‎05-02-2014 12:55 PM

In the original Struts 2 vulnerability documentation, the regular expression also check for the following initial strings followed by a dot:

 

  • dojo
  • struts
  • session
  • request
  • application
  • servletRequest
  • servletResponse
  • parameters
  • action
  • method

Why the regexp for Struts 1 presented here does not include them? Are they relevant?

alvaro_munoz | ‎05-03-2014 09:15 AM

Hi Arturo,

 

Thanks for your comment. Those keywords are relevant if evaluated in the context of the Struts2 Value Stack via OGNL. Struts1 does not use OGNL and has no access to the anything similar to the Value Stack and so they dont suppose a specific risk.

 

Cheers,

Alvaro

Kishore Kirdat(anon) | ‎05-05-2014 02:01 AM

Is it safe to assume that if I am not using ActionForm, but simple using actions alone, then I am not impacted by this vulnerability?

alvaro_munoz | ‎05-05-2014 03:15 AM

Hi Kishore,


Thanks for your comment. If there is no ActionForm associated with the action in the action declaration, then Struts will not start the ActionForm binding process and therefore this particular attack vector will not be possible. However we strongly recommend taking all possible mitigation actions to protect your applciations from unknown attack vectors that may be working on the wild.

 

Cheers,

A

Patrick Trainor(anon) | ‎05-05-2014 09:01 AM

Thanks for making this code available. What license  applies to it if any?

Someone123(anon) on ‎05-06-2014 05:05 AM - last edited on ‎05-06-2014 07:21 AM alvaro_munoz

Here is an improved version of the filter that compiles the pattern only once: https://gist.github.com/anonymous/0045ef4df99b31b43daa

alvaro_munoz | ‎05-06-2014 07:21 AM

Hi Patrick,

 

Thanks for your comment. The fix is not licensed in any way and its completely open source so that any one is free to use it or modify it.
It is provided as-is with no guarantees nor support and designed as a temporal workaround until a patch is made available.


Cheers,

A

pmulay(anon) | ‎05-07-2014 03:02 PM

Can you tell a sample test URL that we can use to test this logic?

alvaro_munoz | ‎05-08-2014 01:14 AM

Thanks for the comment pmulay,

 

Any action associated with an actionForm is vulnerable. URL should look like http://myserver.com/myaction.do

 

Cheers,

A

Jamie W(anon) | ‎05-09-2014 06:10 PM

how do you test if the vulnerability is there and then verify the fix addressed the issue? The main problem I have is I don't know how to test it the vulnerability and thus can't verify if the issue is really addressed after incorporating this fix. Thanks for any help you can provide.

alvaro_munoz | ‎05-10-2014 02:55 AM

Hi Jamie,

 

Thanks for your comment, Im sorry I cannot explain how to verify if the issue has been fixed withouth disclosing the exploit details.

 

All I can say is that the filter has been tested in our labs and verified by the Struts security team.

 

Cheers,

A

William Howard(anon) | ‎05-12-2014 02:16 AM

Thanks for providing the patch!

 

After analysing the regular expression, did you realise that in your regular expression:

 

(.*\.|^|.*|\[('|"))(c|C)lass(\.|('|")]|\[).*

 

the prefix part can be simplified to:

 

(.*)(c|C)lass(\.|('|")]|\[).*

 

Is this still what you intended to do?

 

Regards,

William

 

Reva C(anon) | ‎05-12-2014 02:19 AM
 

I'm using Struts 1.3.10 with Websphere 8. I tried testing the above fix  in my application by providing urls with parameters like "?class.classLoader.defaultAssertionStatus=true", or "class.classLoader.resource.dircontext.docBase=someText" either case all such parameters are permitted into my application. I see those parameters inside the finalParameters returned by the by getParameters(). Am I testing correctly or am I missing something here? I've placed this ParamFilter as the first filter in the chain in my web.xml file. Thanks in advance.

alvaro_munoz | ‎05-12-2014 03:17 AM

Thanks for the comment William,

 

Yes I realized that and I would suggest removing the .* from the first group since otherwise properties ending on "class" wont be accepted. I chose to stick to Struts2 proposed regex and notify them about the extra ".*"

 

Cheers,

A

alvaro_munoz | ‎05-12-2014 03:22 AM

Hi Reva,

 

Thanks for your comment. This filter is a temporal fix for this particular vulnerability. Struts 1 uses getParameterNames to loop through parameters that are going to be bound to ActionForm properties. So in order to mitigate this issue, it is enough to prevent dangerous property names from appearing in the return collection of getParameterNames(). Other request methods like getParameters are not intercepted by the filter and so those parameter will still be available for the servlets except that an ActionForm property mapping will be prevented.

 

Cheers,

A

Reva C(anon) | ‎05-12-2014 05:19 AM

Hi Alvaro,

 

Can you give an example url parameter which will be prevented by this Filter? 

 

Thanks!

alvaro_munoz | ‎05-12-2014 05:25 AM

Hi Reva,

 

The one you posted is an example of a bean property that wont be bound to the actionForm, although a request.getParameters() will return it. The filter will prevent request.getParameterNames() but not request.getParameters(), effectively preventing ActionForm property binding but not other legitimate accesses to the request parameter.

 

Cheers,

A

JFreedom(anon) | ‎05-12-2014 09:51 AM

I have been able to replicate the exploit when using a ActionForm directly, but not when using DynaActionForms. We are using DynaActionForms it appears to use form properties from the struts-config instead of the request.getParameterName during the ActionForm binding process. Is this correct?

alvaro_munoz | ‎05-12-2014 10:29 AM

Hi JFreedom,

 

Thanks for your comment. DynaActionForms use request.getParameterNames() in the same way that ActionForms do. But the bind process is slightly different, DynaActionForms contains a HashMap where the property name and value are stored instead of using apache commons BeanUtils to set up the beans properties.

 

Cheers,

A

Kristina T(anon) | ‎05-13-2014 09:19 AM

Thanks so much for addressing this.

 

You responded to a comment that the regex given by Struts 2 is slightly incorrect, but I was unsure how to apply your comments.  You said to remove the .* from the first group, so would this change the regex to

 

(.*\.|^|\[('|"))(c|C)lass(\.|('|")]|\[).*

 

or something else?  I was finding that in our application, with the original regex, we were able to set property names that ended in "class" just fine but were also able to deny setting those that would affect the class object directly, but I'm wondering if I'm missing something.  Can you please clarify?

 

Thanks!

 

Kristina

alvaro_munoz | ‎05-13-2014 09:39 AM

Hi Kristina,

 

Thank you for your comment. The original regex suggested by the struts team is effectively equivalent to

.*(c|C)lass(\.|('|")]|\[).*

That regex will match and prevent setting nested properties like:  somethingclass.prop

But it will allow setting a property like: simpleclass  (no nesting)

 

The regex that you suggest:

(.*\.|^|\[('|"))(c|C)lass(\.|('|")]|\[).*


will allow to set nested and simple properties that end in "class" but will prevent the classloader manipulation.

 

Thanks,

A

ushasingh(anon) | ‎05-15-2014 03:25 AM

Hello ,

 

Is this ok if I use below filter mapping in given solution.

 

    <filter-mapping>

     <filter-name>ParamFilter</filter-name>
       <url-pattern>*.do</url-pattern>    
    </filter-mapping>

Jamie Wang(anon) on ‎05-15-2014 02:25 PM - last edited on ‎05-16-2014 02:45 AM alvaro_munoz

Hi Alvaro,

 

I understand your comment about not able to detail how to verify the vulnerability. I am wondering if we can discuss this in private. I am working on a product where we need to verify if our product is vulnerable. Not sure if you can reach me via the email address I provided in the form or if possible, I can also be reached at xxx-xxx-xxxx. Thanks in advance.

 

Jamie

 

 

alvaro_munoz | ‎05-16-2014 02:44 AM

Hi ushasingh,

 

That should work if all your actions use the .do suffix

 

Cheers,

A

alvaro_munoz | ‎05-16-2014 02:46 AM

Hi Jamie,

 

Im really sorry, but I cannot give any details on the exploit. Please get in contact with the Struts security team to see if they can help you.

 

Cheers,

A

yours(anon) | ‎06-02-2014 12:11 AM

 Hi Team,

       Thank you for your solution.

       But which name I need to configure in servlet-name

<servlet-name>YOUR ACTION SERVLET</servlet-name>
alvaro_munoz | ‎06-02-2014 01:03 AM

Hi,

 

It depends on how do you name your Struts1 servlet. Look for a servlet definition like:

 

    <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
      ....
  </servlet>

 

The servlet name for this particular example is "action".

 

Thanks,

A

alvaro_munoz | ‎06-06-2014 04:01 AM

Hi Nilesh,

 

Thank you for your comment. The original regex suggested by the struts team will match and prevent setting nested properties like:  myclass.something

But it will allow setting a property like: myclass  (no nesting)

 

The following regex will allow to set nested and simple properties that end in "class" but will prevent the classloader manipulation.

(.*\.|^|\[('|"))(c|C)lass(\.|('|")]|\[).*



Thanks,

A

shigee | ‎06-16-2014 06:59 PM

Hi,

 

Workarounds of cookie was necessary in Struts2.

In Struts1, is similar Workarounds unnecessary?

 

 

Thanks!

alvaro_munoz | ‎06-17-2014 01:40 AM

Hi Shigee,

 

No, this particular bug only affects Action Form bindings

 

Cheers,

A

Tomari Rohai(anon) | ‎06-18-2014 07:16 AM

I've coded this, tested it, and I can see the bit of code that lets parameters through is being used, by examining the log.

 

So negatively, I can see this is working, no problemo.

 

How can I test a positive (ie, that somehing is being trapped by the code)? I dont want to know anything about the exploit, but I need a strategy whereby I can show this code is working.

 

Any ideas? Am I missing something glaringly obvious here? Me missing something obvious is always the default position.

 

Cheers,

 

Tom

alvaro_munoz | ‎06-18-2014 08:21 AM

Hi Tom,

 

Thanks for your comment. If the filter works ok and its correctly deployed, it should be preventing access to certain parameters. The parameters that are going to be intercepted are the ones in that match the regular expression. So for example if you have a Action form with a user field of type "User", then trying to set the nested property "user.class" should be trapped by the filter and logged.

 

Cheers,

A

Martin Gainty(anon) | ‎07-09-2014 05:15 PM
struts.debug=true
1)You *should* see the parameters trapped by your regex with the System.out:
if (!parameterName.matches(regex)) { finalParameterNames.add(parameterName); System.out.println("Param included is : " + parameterName); }
else { System.out.println("Param excluded is : "+parameterName); }
2)Shark your line if you want to see the traffic coming and going

Saludos Cordiales desde EEUU,
Martín

 

Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
Showing results for 
Search instead for 
Do you mean 
About the Author
Featured


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation.