Java反序列化 | CC链专题
字数 1506 2025-08-20 18:18:16

Java反序列化漏洞与CC链分析

一、序列化与反序列化基础

1.1 序列化(Serialization)

序列化是将对象状态转化为字节流的机制,只有实现了SerializableExternalizable接口的类的对象才能被序列化。

序列化关键点:

  • 使用ObjectOutputStreamwriteObject()方法
  • 静态成员变量不可被序列化
  • transient标识的成员变量不参与序列化

序列化示例:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
oos.writeObject(obj);

1.2 反序列化(Deserialization)

反序列化是将字节流重新创建为Java对象的过程。

反序列化关键点:

  • 使用ObjectInputStreamreadObject()方法
  • 反序列化时会自动调用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中,主要类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)
  2. 扩展类加载器(Extension ClassLoader)
  3. 应用类加载器(Application ClassLoader)
  4. 自定义类加载器

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()

利用步骤:

  1. 创建InvokerTransformer实例,设置反射调用Runtime.exec()
  2. 使用TransformedMap.decorate()包装Map对象
  3. 通过反射创建AnnotationInvocationHandler实例
  4. 序列化和反序列化触发漏洞

五、防御措施

  1. 输入验证:对反序列化的数据来源进行严格验证
  2. 白名单控制:限制可反序列化的类
  3. 安全配置
    • 使用ObjectInputFilter设置反序列化过滤器
    • 更新JDK和依赖库到安全版本
  4. 代码审计:检查自定义readObject方法的安全性
  5. 运行时保护:使用安全管理器限制敏感操作

六、总结

Java反序列化漏洞的核心在于:

  1. 重写readObject方法引入不安全操作
  2. 利用反射机制动态调用危险方法
  3. 通过精心构造的利用链连接入口点和危险函数

理解这些机制对于安全研究和防御Java反序列化漏洞至关重要。在实际应用中,应避免直接反序列化不可信数据,并及时更新相关组件以修复已知漏洞。

Java反序列化漏洞与CC链分析 一、序列化与反序列化基础 1.1 序列化(Serialization) 序列化是将对象状态转化为字节流的机制,只有实现了 Serializable 或 Externalizable 接口的类的对象才能被序列化。 序列化关键点: 使用 ObjectOutputStream 和 writeObject() 方法 静态成员变量不可被序列化 transient 标识的成员变量不参与序列化 序列化示例: 1.2 反序列化(Deserialization) 反序列化是将字节流重新创建为Java对象的过程。 反序列化关键点: 使用 ObjectInputStream 和 readObject() 方法 反序列化时会自动调用 readObject() 方法 反序列化示例: 1.3 反序列化漏洞原理 反序列化漏洞产生的根本原因是 readObject 方法被重写,当反序列化时会自动调用重写的 readObject 方法,如果其中包含恶意代码就会被执行。 漏洞示例: 二、反射机制 2.1 反射基础 反射允许程序在运行时检查和操作类的成员,包括: 获取类的名称、属性、方法、注解等信息 动态修改类的属性 调用对象的方法 2.2 反射关键操作 1. 获取Class对象: 2. 实例化对象: 3. 获取和修改属性: 4. 调用方法: 三、Java类加载机制 3.1 类加载器 Java类加载器负责将类的字节码加载到JVM中,主要类加载器: 启动类加载器(Bootstrap ClassLoader) 扩展类加载器(Extension ClassLoader) 应用类加载器(Application ClassLoader) 自定义类加载器 3.2 动态类加载方式 1. Class.forName() 2. ClassLoader.loadClass() 3. URLClassLoader 4. defineClass 四、反序列化利用链(CC链)分析 4.1 URLDNS链分析 URLDNS链是Java反序列化的简单利用链,用于DNS探测。 利用链: 利用代码: 4.2 CC1链分析 CC1链利用Apache Commons Collections库中的Transformer接口。 关键类: InvokerTransformer : 实现Transformer接口,可通过反射调用任意方法 TransformedMap : 可包装Map对象,在修改值时调用Transformer 利用链: 利用步骤: 创建 InvokerTransformer 实例,设置反射调用 Runtime.exec() 使用 TransformedMap.decorate() 包装Map对象 通过反射创建 AnnotationInvocationHandler 实例 序列化和反序列化触发漏洞 五、防御措施 输入验证 :对反序列化的数据来源进行严格验证 白名单控制 :限制可反序列化的类 安全配置 : 使用 ObjectInputFilter 设置反序列化过滤器 更新JDK和依赖库到安全版本 代码审计 :检查自定义 readObject 方法的安全性 运行时保护 :使用安全管理器限制敏感操作 六、总结 Java反序列化漏洞的核心在于: 重写 readObject 方法引入不安全操作 利用反射机制动态调用危险方法 通过精心构造的利用链连接入口点和危险函数 理解这些机制对于安全研究和防御Java反序列化漏洞至关重要。在实际应用中,应避免直接反序列化不可信数据,并及时更新相关组件以修复已知漏洞。