保姆级讲解CC1-7(跟踪代码调试+讲解)
字数 4258
更新时间 2026-03-12 13:53:13
Java反序列化漏洞利用链(CC1-7)保姆级教学文档
1. Java原生序列化与反序列化
1.1 核心概念
序列化:将一个Java对象转换成字节流、字符串或二进制文件的形式,用于对象的传输与持久化。
反序列化:将字节、文本数据还原成Java对象。
1.2 应用场景
- 对象持久化保存到本地文件
- 网络通信中传输对象数据
- Java的序列化方案包括:
- 原生实现
- 基于XML、SOAP、JSON
- Protocol Buffers (Protobuf)
- Thrift
- Hessian
1.3 简单示例
// Person类必须实现Serializable接口
class Person implements Serializable {
private String name;
private int age;
// 构造方法、getter/setter、toString()省略
}
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"));
oos.writeObject(person);
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"));
Person p = (Person) ois.readObject();
ois.close();
1.4 关键特性
- Serializable接口:标记接口,无方法需要实现
- 静态成员变量:不能被序列化(属于类本身,而非对象实例)
- transient关键字:标记的成员变量不参与序列化
- 自定义序列化:可重写
writeObject()和readObject()方法控制序列化逻辑
1.5 安全风险
服务端对恶意序列化对象执行反序列化时,会自动执行readObject()方法中的代码,为攻击者提供了在服务器上执行任意代码的能力。
2. 反序列化漏洞利用原理
2.1 利用条件
要实现完整的反序列化攻击,需要三部分:
-
入口类(Gadget):
- 实现Serializable接口
- 重写
readObject()方法 - 方法中调用某个通用方法(如
hashCode()、equals()、toString()) - 最好是JDK自带类,通用性强
-
调用链(Chain):
- 从入口类的通用方法开始
- 通过"相同方法名+兼容类型"层层传递
- 最终调用到危险方法
-
执行点(Sink):
- 最终执行恶意操作的代码位置
- 如RCE、SSRF、文件写入等
2.2 常见入口类
- HashMap:
readObject()中调用hash()→key.hashCode() - Hashtable:
readObject()中调用key.equals() - PriorityQueue:
readObject()中调用比较器的compare() - BadAttributeValueExpException:
readObject()中调用val.toString()
3. 核心技术详解
3.1 反射(Reflection)
核心:反向获取与操作类的所有内容,包括私有成员。
常用操作:
- 通过Class对象实例化对象
- 获取/修改类属性(包括私有)
- 获取/调用类方法(包括私有)
在反序列化中的作用:
- 修改对象属性值,构造利用链
- 动态调用任意方法
- 绕过WAF过滤(方法名拆分拼接)
关键方法:
Class.forName("类全限定名"):获取Class对象getDeclaredField():获取所有属性(包括私有)setAccessible(true):突破访问限制getMethod()/invoke():获取并调用方法
3.2 动态代理
核心模式:不直接访问原对象,通过代理对象间接访问,在不修改原代码前提下扩展功能。
JDK动态代理实现:
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强逻辑
return method.invoke(target, args);
}
};
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
在反序列化中的作用:
- 隐式自动执行:代理对象任何方法被调用都会进入
invoke() - 灵活拼接调用链:无论入口类调用什么方法,都能通过代理走到目标逻辑
3.3 类动态加载
核心:从字节码动态加载类并执行代码。
类加载流程:
loadClass() → 双亲委派 → findClass() → defineClass()(native)
常用类加载器:
- Bootstrap ClassLoader:加载JDK核心库
- Extension ClassLoader:加载JRE扩展库
- Application ClassLoader:加载项目classpath
动态加载方法:
- URLClassLoader:从本地/远程路径加载类
- ClassLoader.defineClass():直接从字节码数组加载(需反射调用)
- Unsafe.defineClass():底层方法(需反射获取Unsafe实例)
关键类:TemplatesImpl
- 实现Serializable接口
newTransformer()方法会加载字节码并实例化类- 用于动态加载恶意类执行代码
4. Commons Collections利用链详解
4.1 Commons Collections核心接口
Transformer接口:
public interface Transformer {
Object transform(Object input);
}
关键实现类:
- ConstantTransformer:无论输入什么,都返回固定常量
- InvokerTransformer:封装反射调用,可调用任意对象的任意方法
- ChainedTransformer:将多个Transformer链式调用
- InstantiateTransformer:调用构造方法实例化对象
4.2 CC1链(TransformedMap版)
完整调用链:
AnnotationInvocationHandler.readObject()
→ Map.Entry.setValue()
→ TransformedMap.checkSetValue()
→ ChainedTransformer.transform()
→ ConstantTransformer.transform() // 返回Runtime.class
→ InvokerTransformer.transform() // 反射调用getRuntime()
→ InvokerTransformer.transform() // 反射调用getRuntime()
→ InvokerTransformer.transform() // 反射调用exec()
构造要点:
- 用
TransformedMap.decorate()装饰Map,valueTransformer设为恶意ChainedTransformer - AnnotationInvocationHandler构造时传入恶意Map
- Map的key必须与注解实际属性名匹配
- 用ConstantTransformer固定输入为Runtime.class
4.3 CC1链(LazyMap版/ysoserial原版)
调用链:
AnnotationInvocationHandler.readObject()
→ memberValues.get() // 动态代理触发invoke()
→ LazyMap.get()
→ factory.transform() // 即ChainedTransformer
特点:
- 使用LazyMap,只有key不存在时才调用transform
- 两层AnnotationInvocationHandler嵌套
- 内层作为动态代理的InvocationHandler
- 代理Map接口,外层接收代理对象
JDK 8u71修复:readObject()中创建新LinkedHashMap操作,不直接操作传入的Map
4.4 CC6链(通用性强)
调用链:
HashMap.readObject()
→ key.hashCode() // key为TiedMapEntry
→ TiedMapEntry.hashCode()
→ TiedMapEntry.getValue()
→ map.get() // map为LazyMap
→ factory.transform()
优势:
- 不受JDK版本限制
- 入口类为HashMap,极为常见
- 兼容性极强
构造要点:
- 构造LazyMap时先传无害Transformer
- 创建TiedMapEntry(key为LazyMap中不存在的值)
- 将TiedMapEntry作为HashMap的key
- put完成后,反射修改LazyMap.factory为恶意Transformer
- 调用
lazyMap.remove(key)删除测试key
4.5 CC3链(动态类加载版)
核心:用TemplatesImpl动态加载字节码执行代码,绕过Runtime黑名单检测。
TemplatesImpl关键属性:
_bytecodes:恶意类字节码数组(byte[][])_name:任意非空字符串_tfactory:transient修饰,反序列化时会自动赋值- 恶意类必须继承
AbstractTranslet
两种实现方式:
- 简化版:直接用InvokerTransformer调用
newTransformer() - 原版:用InstantiateTransformer实例化TrAXFilter,其构造方法调用
templates.newTransformer()
4.6 CC4链(Commons Collections 4)
调用链:
PriorityQueue.readObject()
→ heapify() → siftDown() → siftDownUsingComparator()
→ comparator.compare() // comparator为TransformingComparator
→ transformer.transform()
特点:
- 针对Commons Collections 4.x
- TransformingComparator在CC4中实现了Serializable
- 需向PriorityQueue添加至少2个元素(size≥2)
- 构造时先传无害Transformer,add完成后反射修改
4.7 CC2链(无数组版)
特点:
- 针对Commons Collections 4.x
- 不依赖ChainedTransformer数组
- 直接用InvokerTransformer调用
newTransformer() - 在某些环境(如重写类加载)中更有优势
4.8 CC5链
调用链:
BadAttributeValueExpException.readObject()
→ val.toString() // val为TiedMapEntry
→ TiedMapEntry.toString()
→ getValue() → map.get()
特点:toString()触发,与CC6类似
4.9 CC7链
调用链:
Hashtable.readObject()
→ reconstitutionPut()
→ key.equals() // key为AbstractMapDecorator
→ AbstractMap.equals()
→ map.get()
特点:equals()触发,涉及哈希碰撞技巧
5. URLDNS链(漏洞验证)
最简单利用链,用于验证反序列化漏洞存在。
调用链:
HashMap.readObject()
→ hash() → key.hashCode() // key为URL对象
→ URL.hashCode()
→ URLStreamHandler.hashCode()
→ getHostAddress() // 发起DNS解析
构造要点:
- 反射修改URL对象的hashCode属性(先改非-1,put后再改回-1)
- 避免put时本地触发DNS请求
- 纯JDK类,通用性极强
6. 防御与绕过
6.1 常见防护
- 输入过滤:检查反序列化数据
- 黑名单:过滤危险类(如InvokerTransformer、TemplatesImpl)
- 白名单:只允许特定类反序列化
- JEP 290:Java的反序列化过滤器
- 升级依赖:修复已知漏洞版本
6.2 绕过思路
- 多态调用:利用接口/父类类型调用子类方法
- 动态代理:统一入口,灵活跳转
- 类加载绕过:动态加载字节码,不依赖已有类
- 非数组链:如CC2,避免数组处理问题
- 新入口类:寻找未防护的入口点
7. 实践建议
7.1 学习路径
- 掌握Java序列化基础
- 深入理解反射、动态代理、类加载
- 调试CC6链(兼容性最好)
- 掌握CC3链(动态加载绕过)
- 了解其他链变种
7.2 调试技巧
- 手敲代码,避免复制粘贴
- 在关键方法设断点(readObject、transform、invoke等)
- 关注if判断条件,满足执行路径
- 注意transient变量和静态变量
- 理解每个类在链中的作用
7.3 实际应用
- 漏洞验证:URLDNS链简单有效
- 命令执行:CC1、CC6 + Runtime.exec
- 代码执行:CC3 + TemplatesImpl(绕过能力强)
- 特殊环境:CC2(无数组)、CC4(CC4版本)
8. 总结
Java反序列化漏洞的核心在于控制数据、控制代码执行。通过精心构造的序列化对象,在反序列化过程中触发预设的调用链,最终实现任意代码执行。
关键技术栈:
- 序列化机制 → 数据载体
- 反射 → 动态操作
- 动态代理 → 灵活跳转
- 类加载 → 代码执行
- Commons Collections → 调用链组件
实用链选择:
- 学习入门:CC1(理解完整流程)
- 实际测试:CC6(兼容性最佳)
- 绕过防护:CC3(动态加载)
- 特殊场景:CC2(无数组需求)
掌握这些知识不仅有助于理解反序列化漏洞,更能深入理解Java安全机制、类加载原理、设计模式应用等多个层面的知识体系。
相似文章
相似文章