漏洞概要
可参考官方安全公告:https://cwiki.apache.org/confluence/display/WW/S2-002
漏洞分析
通过官网的安全公告,我们大概知道问题是出在标签<s:url>
和 <s:a>
标签
中,如下是我们的index.jsp
代码
1 | <%@ taglib prefix="s" uri="/struts-tags" %> |
由于s2的标签库都是集成与ComponentTagSupport
类,doStartTag
方法也是在该类里实现,所以我们直接从ComponentTagSupport
类doStartTag
方法进行断点调试, 首先我们看一下doStartTag
方法:
由于我们这里处理的是 s:url
标签,所以这里用来处理标签的组件 this.component
为org.apache.struts2.components.URL
类对象。我们跟进 URL:start()
方法。
在 URL:start()
方法中,我们看到当includeParams=all
时,会调用 mergeRequestParameters
方法。
在 mergeRequestParameters
方法中,程序会将 this.req.getParameterMap()
获得的键值对数据存入 this.parameters
属性。
getParameterMap()
返回一个map类型的request参数
1 | http://192.168.174.1:8888/Struts2_demo_war_exploded/?<script>alert(1)</script> |
那么解析后的map就是 : key= <script>alert(1)</script>、vaule = ""
并未看到对参数进行任何过滤,
getParameterMap()方法并不会对数据进行任何处理。可见下文demo实例
最后进入doEndTag
方法进行处理
determineActionURL
方法中调用了URLHelper
类处理 this.parameters
数据并进行返回
将其写入,导致XSS漏洞。
includeParams=get
时并不能触发 XSS 漏洞。
主要原因在于:当includeParams=all
时,会多执行一个mergeRequestParameters
方法,而该方法会将 this.req.getParameterMap()
数据设置到this.parameters
。如果 includeParams=get
,那么 this.parameters
中的数据,仅是来自 this.req.getQueryString()
而 this.req.getParameterMap()
获得的数据会主动进行URLdecode
,但是this.req.getQueryString()
不会。所以 includeParams=get
时,返回的数据是被 URLencode
过的,因此不能触发 XSS 漏洞。可见下文demo实例
demo实例
demo实例
1 | package com.test; |
Poc:
1 | ?<script>alert(1)</script> |
修复
根据公告,我们需要升级到Struts 2.0.11.1版本,未真正修复,仅仅是对script标签进行替换,仍然可以对其进行绕过
bypass POC:
1 | ?<script 1>alert(1)</script> |