一、原理
(一)概述
参见官方通告,特定版本的Apache Struts中的<s:url>
和<s:a>
标签会引发XSS漏洞。
读者范围 | 所有Struts 2开发者 |
---|---|
漏洞影响 | 客户端恶意代码注入 |
影响程度 | 重要 |
修复建议 | 更新至Struts 2.2.1 |
受影响的版本 | Struts 2.0.0 – Struts 2.1.8.1 |
(二)原理
在返回的页面被渲染时,<s:url>
和<s:a>
标签存在一定的可能性被注入未被合适转义的参数值。如下面的场景:
- 一个
<s:a>
标签被建立时,其中的参数值可以注入一个未被转义的双引号,如此则可以通过转义<href>
标签注入生成的HTML。 - 当
includeParams
的值被设为非”none”时,<s:url>
和<s:a>
标签未能转义<sc ript>
标签,此时相应的JSP/action可能会被恶意的GET 参数破坏,例如http://localhost/foo/bar.action?<sc ript>alert(1)</sc ript>test=hello
二、调试
(一)环境搭建
因为要修改源码,短浅的想了想无法直接使用拿来主义,需要自己动手搭建(参考此链接),可在官网下载Tomcat,然后从此处中下载所需要的struts2,。
然后新建Project,Use Library中选择下载好的Struts2的lib。
进入Project Structure->Artifacts,点击下图红框中的那个选项,点击后会成为下图的样子。
接下来准备相关代码文件,首先src下新建struts.xml如下,
<?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>
<package name="S2-001" extends="struts-default">
<action name="login" class="com.demo.action.LoginAction">
<result name="success">welcome.jsp</result>
<result name="error">index.jsp</result>
</action>
</package>
</struts>
接下来修改web.xml如下,
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>S2-001 Example</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
选择Build->Build Project。
若正常则点击run,会有如下窗口。
(二)复现
修改Tomcat Server的URL配置,
run,可得,
可见XSS漏洞复现成功。
(三)调试
接下来以debug模式运行Tomcat Server,
可见可以在doStartTag处断下。
此时点击调用栈,也可以查看jsp内的流程了,
环境应该好了,可以准备调试Tomcat了。
由之前的学习可知,Struts开始解析jsp里的标签时,会调用ComponentTagSupport.doStartTag(),
此时的compnent为URL类,对应jsp里的url。
跟进this.component.start(),
可以看出,这里会将参数includeParams
提取出来,为下面的流程做准备,
接下来会进行匹配,此处我们设置的includeParams
值为all,故而会进入mergeRequestParameters(),
先看看 this.request.getParameterMap(),
多级步入之后,发现是从Request中提取参数,
跟进mergeRequestParameters,
这个函数名已经表示了它的功能,大概就是合并请求的各个参数,
跟进之,此时的Map mergedParams 的size为1,值为<sc ript>alert("1");</sc ript> -> 1
,这时可以看到程序在读取了参数后,迭代添加到parameters里面去,
步出后,parameters的size为1,
接下来进入includeGetParameters,
这里能看到一点不同,
就是这里的query是urlencode之后的形式,
includeGetParameters这个函数的名字也告诉了我们它的功能,应该是将GET参数也添加进来。
解析标签,还需要调用doEndTag,
跟进end,
此处的一个关键点是determineActionURL,从函数名可以大致猜出,此函数是确定一个action对应出的url的,跟进,
这里面先将login.action的名字添加进去,接下来有一个buildParametersString函数,
因为在determineActionURL内,所以不难猜出这个函数的功能即是将url尾部的参数确定下来,跟进,
走到添加参数的地方,
current是未经url编码的,next是经过url编码的,其间要加一个连接符(或曰分隔符),
最终效果,
步出,
回到URL.end,
接下来就是将url输出。
另外,includeParameters必须为all,不能像官方通告里那样不为none即可,因为从调试过程中我们可以观察到,
若includeParameters为get,则只会进行一次includeGetParameters,而缺少if all分支中的mergeQequestParameters部分,且在刚才的调试过程中我们看到,includeGetParameters添加进来的参数是url编码过的,要想让页面上有<sc ript>xxxx</sc ript>
的字符,还是得有if all 分支中独立的mergeQequestParameters的函数。
三、收获与启示
参考链接
https://www.cnblogs.com/twosmi1e/p/14134511.html
https://dean2021.github.io/posts/s2-002/
发表评论
您还未登录,请先登录。
登录