Java反序列化 | CC链专题
字数 1506 2025-08-20 18:18:16
Java反序列化漏洞与CC链分析
一、序列化与反序列化基础
1.1 序列化(Serialization)
序列化是将对象状态转化为字节流的机制,只有实现了Serializable或Externalizable接口的类的对象才能被序列化。
序列化关键点:
- 使用
ObjectOutputStream和writeObject()方法 - 静态成员变量不可被序列化
transient标识的成员变量不参与序列化
序列化示例:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
oos.writeObject(obj);
1.2 反序列化(Deserialization)
反序列化是将字节流重新创建为Java对象的过程。
反序列化关键点:
- 使用
ObjectInputStream和readObject()方法 - 反序列化时会自动调用
readObject()方法
反序列化示例:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
return ois.readObject();
1.3 反序列化漏洞原理
反序列化漏洞产生的根本原因是readObject方法被重写,当反序列化时会自动调用重写的readObject方法,如果其中包含恶意代码就会被执行。
漏洞示例:
private void readObject(ObjectInputStream ois) throws Exception {
ois.defaultReadObject();
Runtime.getRuntime().exec("calc"); // 恶意代码
}
二、反射机制
2.1 反射基础
反射允许程序在运行时检查和操作类的成员,包括:
- 获取类的名称、属性、方法、注解等信息
- 动态修改类的属性
- 调用对象的方法
2.2 反射关键操作
1. 获取Class对象:
Class<?> clazz = Class.forName("ClassName");
2. 实例化对象:
// 无参构造
Object obj = clazz.newInstance();
// 有参构造
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
Object obj = constructor.newInstance("参数1", 2);
3. 获取和修改属性:
// 获取属性
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // 对私有属性需要设置可访问
// 修改属性值
field.set(obj, "newValue");
4. 调用方法:
// 获取方法
Method method = clazz.getDeclaredMethod("methodName", parameterTypes);
method.setAccessible(true); // 对私有方法需要设置可访问
// 调用方法
method.invoke(obj, args);
三、Java类加载机制
3.1 类加载器
Java类加载器负责将类的字节码加载到JVM中,主要类加载器:
- 启动类加载器(Bootstrap ClassLoader)
- 扩展类加载器(Extension ClassLoader)
- 应用类加载器(Application ClassLoader)
- 自定义类加载器
3.2 动态类加载方式
1. Class.forName()
Class<?> clazz = Class.forName("ClassName");
2. ClassLoader.loadClass()
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class<?> clazz = cl.loadClass("ClassName");
3. URLClassLoader
URLClassLoader cl = new URLClassLoader(new URL[]{new URL("file:///path/")});
Class<?> clazz = cl.loadClass("ClassName");
4. defineClass
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(cl, "ClassName", code, 0, code.length);
四、反序列化利用链(CC链)分析
4.1 URLDNS链分析
URLDNS链是Java反序列化的简单利用链,用于DNS探测。
利用链:
HashMap.readObject()
-> HashMap.putVal()
-> HashMap.hash()
-> URL.hashCode()
-> URLStreamHandler.hashCode()
-> URLStreamHandler.getHostAddress()
-> DNS解析
利用代码:
URL url = new URL("http://example.com");
HashMap<URL, Object> hashMap = new HashMap<>();
hashMap.put(url, null);
// 反射修改URL的hashCode值为-1
Field hCode = URL.class.getDeclaredField("hashCode");
hCode.setAccessible(true);
hCode.setInt(url, -1);
// 序列化和反序列化触发DNS请求
serialize(hashMap, "dns.bin");
unserialize("dns.bin");
4.2 CC1链分析
CC1链利用Apache Commons Collections库中的Transformer接口。
关键类:
InvokerTransformer: 实现Transformer接口,可通过反射调用任意方法TransformedMap: 可包装Map对象,在修改值时调用Transformer
利用链:
AnnotationInvocationHandler.readObject()
-> Map.entrySet().iterator().next().setValue()
-> TransformedMap.checkSetValue()
-> Transformer.transform()
-> InvokerTransformer.transform()
-> Runtime.exec()
利用步骤:
- 创建
InvokerTransformer实例,设置反射调用Runtime.exec() - 使用
TransformedMap.decorate()包装Map对象 - 通过反射创建
AnnotationInvocationHandler实例 - 序列化和反序列化触发漏洞
五、防御措施
- 输入验证:对反序列化的数据来源进行严格验证
- 白名单控制:限制可反序列化的类
- 安全配置:
- 使用
ObjectInputFilter设置反序列化过滤器 - 更新JDK和依赖库到安全版本
- 使用
- 代码审计:检查自定义
readObject方法的安全性 - 运行时保护:使用安全管理器限制敏感操作
六、总结
Java反序列化漏洞的核心在于:
- 重写
readObject方法引入不安全操作 - 利用反射机制动态调用危险方法
- 通过精心构造的利用链连接入口点和危险函数
理解这些机制对于安全研究和防御Java反序列化漏洞至关重要。在实际应用中,应避免直接反序列化不可信数据,并及时更新相关组件以修复已知漏洞。