漏洞概要
可参考官方安全公告: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> |