Struts2框架: S2-002 漏洞详细分析

栏目: Java · 发布时间: 6年前

内容简介:阅读本文需要具备的知识:影响漏洞版本:漏洞靶机代码: (下方通过该代码进行分析, 务必下载本地对比运行)

0x00 前言

阅读本文需要具备的知识:

  1. 熟悉J2EE开发, 主要是JSP开发
  2. 了解Struts2框架执行流程

0x01 漏洞复现

影响漏洞版本:

Struts 2.0.0 - Struts 2.0.11

漏洞靶机代码: (下方通过该代码进行分析, 务必下载本地对比运行)

https://github.com/dean2021/java_security_book/tree/master/Struts2/s2_002

测试POC:

http://localhost:8080/index.action?"><script>alert(1)</script><"

请求响应内容:

<body>

	<a href="//hello/hello_struts2.action?"><script>alert(1)</script><"=&%22%3E%3Cscript%3Ealert(1)%3C/script%3E%3C%22=">ä½ å¥½Struts2</a>

</body>

0x02 漏洞分析

通过官网安全公告 参考[1],我们大概知道问题是出在 标签里,如下是我们的index.jsp部分代码:

<!DOCTYPE html PUBLIC 
	"-//W3C//DTD XHTML 1.1 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
	
<%@taglib prefix="s" uri="/struts-tags" %>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<body>
	<a href="<s:url action="/hello/hello_struts2" includeParams="all" ></s:url>">你好Struts2</a>
</body>
</html>

两个标签我们就分析 一个就行了,读过我上篇文章的同学应该知道我们先从找到标签的实现对象入手,这里就不多说了,由于s2的标签库都是集成与ComponentTagSupport类, doStartTag方法也是在该类里实现,所以我们直接从ComponentTagSupport类doStartTag方法进行断点调试, 首先我们看一下doStartTag方法:

public abstract class ComponentTagSupport extends StrutsBodyTagSupport {

	 public int doStartTag() throws JspException {

	 	// 实现子类是URL.class
        this.component = this.getBean(this.getStack(), (HttpServletRequest)this.pageContext.getRequest(), (HttpServletResponse)this.pageContext.getResponse());
        Container container = Dispatcher.getInstance().getContainer();
        container.inject(this.component);
        this.populateParams();

        // 跟进URL类的start方法实现
        boolean evalBody = this.component.start(this.pageContext.getOut());
        if (evalBody) {
            return this.component.usesBody() ? 2 : 1;
        } else {
            return 0;
        }
    }

跟进URL类的start方法实现:

public class URL extends Component {

	public boolean start(Writer writer) {
	        boolean result = super.start(writer);
	        if (this.value != null) {
	            this.value = this.findString(this.value);
	        }

	        try {

	        	// 我们在<s:url>这个标签内配置的includeParams="all"
	        	// 关于这个属性介绍,参考2
	            String includeParams = this.urlIncludeParams != null ? this.urlIncludeParams.toLowerCase() : "get";
	            if (this.includeParams != null) {
	                includeParams = this.findString(this.includeParams);
	            }


	            if ("none".equalsIgnoreCase(includeParams)) {
	                this.mergeRequestParameters(this.value, this.parameters, Collections.EMPTY_MAP);
	            } else if ("all".equalsIgnoreCase(includeParams)) {

                    // 我们跟进此方法的实现
	                this.mergeRequestParameters(this.value, this.parameters, this.req.getParameterMap());
	                
	                this.includeGetParameters();
	                this.includeExtraParameters();
	            } else if (!"get".equalsIgnoreCase(includeParams) && (includeParams != null || this.value != null || this.action != null)) {
	                if (includeParams != null) {
	                    LOG.warn("Unknown value for includeParams parameter to URL tag: " + includeParams);
	                }
	            } else {
	                this.includeGetParameters();
	                this.includeExtraParameters();
	            }
	        } catch (Exception var4) {
	            LOG.warn("Unable to put request parameters (" + this.req.getQueryString() + ") into parameter map.", var4);
	        }

	        return result;
	    }

this.mergeRequestParameters(this.value, this.parameters, this.req.getParameterMap()); 跟进实现:

protected void mergeRequestParameters(String value, Map parameters, Map contextParameters) {
        Map mergedParams = new LinkedHashMap(contextParameters);
        if (value != null && value.trim().length() > 0 && value.indexOf("?") > 0) {
            new LinkedHashMap();
            String queryString = value.substring(value.indexOf("?") + 1);
            mergedParams = UrlHelper.parseQueryString(queryString);
            Iterator iterator = contextParameters.entrySet().iterator();

            while(iterator.hasNext()) {
                Entry entry = (Entry)iterator.next();
                Object key = entry.getKey();
                if (!((Map)mergedParams).containsKey(key)) {
                    ((Map)mergedParams).put(key, entry.getValue());
                }
            }
        }

        Iterator iterator = ((Map)mergedParams).entrySet().iterator();

        while(iterator.hasNext()) {
            Entry entry = (Entry)iterator.next();
            Object key = entry.getKey();
            if (!parameters.containsKey(key)) {
                parameters.put(key, entry.getValue());
            }
        }

}

从方法明明上我们已经能够看得出该方法是合并参数,通过阅读代码该方法的第三个参数也就是HttpServletRequest对象getParameterMap(), HttpServletRequest是Servlet原生对象,那这个方法具体是用来做什么的呢?下方是官方解释:

Returns a java.util.Map of the parameters of this request.

也就是返回一个map类型的request参数。我们请求的是url是:

http://localhost:8080/index.action?"><script>alert(1)</script><"

那么解析后的map就是 : KEY = "><script>alert(1)</script><" VAL = “” 然后进行参数合并, 并未看到对参数进行任何过滤,最后写入到html中,导致造成xss漏洞。

TIPS: 经过测试HttpServletRequest对象getParameterMap()方法只会对参数值进行转换编码,并不会对参数名进行任何处理.

0x03 总结:

Struts2框架的<s:url>标签的includeParams属性设置为all的情况下,对url参数名未做过滤,导致xss漏洞。

0x04 修复方案分析:

根据公告,我们需要升级到Struts 2.0.11.1版本。

经过对2.0.11.1的代码阅读,在UrlHelper类buildUrl方法里,第136行增加了如下修复代码:

// link是最终的生成的url
        for(result = link.toString(); result.indexOf("<script>") > 0; result = result.replaceAll("<script>", "script")) {

        }

看到这样的修复,虽然很无语,但是站在没有web安全知识的 程序员 角度来看待这种修复方案,能这样写也是很正常,因为大部分程序员只知道JavaScript代码是在 <script> 标签中执行。

好了,分析结束,附上一个bypass POC:

index.action?"><script 1>alert(1)</script>"

下一篇S2-003应该就是修复这个问题吧。

0x06 引用


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ActionScript 3.0 Cookbook

ActionScript 3.0 Cookbook

Joey Lott、Darron Schall、Keith Peters / Adobe Dev Library / 2006-10-11 / GBP 28.50

Well before Ajax and Microsoft's Windows Presentation Foundation hit the scene, Macromedia offered the first method for building web pages with the responsiveness and functionality of desktop programs......一起来看看 《ActionScript 3.0 Cookbook》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器