深度 - Java 反序列化 Payload 之 JRE8u20
字数 1713 2025-08-29 08:31:47

Java反序列化漏洞深入分析:JRE8u20 Payload详解

1. 背景知识

1.1 Jdk7u21 Payload回顾

Jdk7u21 Payload是一个经典的Java反序列化漏洞利用链,其核心原理如下:

  1. TemplatesImpl类:可序列化,内部__bytecodes成员可存储class字节码
  2. 执行流程
    • 通过TemplatesImpl.getOutputProperties()方法触发
    • __bytecodes存储的字节码会被转换为Class对象(通过ClassLoader.defineClass)
    • 实例化该Class,执行构造方法中的代码
  3. 触发机制:利用LinkedHashSetAnnotationInvocationHandler触发getOutputProperties

1.2 Jdk7u21的修补

在7u21之后的版本中,AnnotationInvocationHandler.readObject方法增加了检查:

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    AnnotationType var2 = null;
    try {
        var2 = AnnotationType.getInstance(this.type);
    } catch (IllegalArgumentException var9) {
        throw new InvalidObjectException("Non-annotation type in annotation serial stream");
    }
    // 省略后续代码
}

关键点:

  • 检查this.type是否为注解类型,非注解类型则抛出异常
  • 异常抛出前已经通过defaultReadObject()还原了对象
  • 这使得this.type必须为Templates.class的7u21 Payload失效

2. 绕过思路

2.1 核心发现

AnnotationInvocationHandler.readObject的执行顺序:

  1. 先通过var1.defaultReadObject()还原对象
  2. 然后检查this.type并可能抛出异常

这意味着:

  • 对象已经被成功还原
  • 异常抛出后对象仍然存在
  • 可以通过引用机制在其他地方使用这个对象

2.2 关键技术点

2.2.1 Java序列化引用机制

Java序列化会为每个对象分配一个handle,重复对象会使用TC_REFERENCE引用已序列化的对象。

示例:

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("/tmp/ser")));
Date d = new Date();
out.writeObject(d);  // 写入实际对象
out.writeObject(d);  // 写入引用
out.close();

序列化结构:

TC_OBJECT - 第一个Date对象 (分配handle 0x00 7e 00 01)
TC_REFERENCE - 引用handle 0x00 7e 00 01

2.2.2 异常处理技巧

通过包装类捕获异常,使"非法"对象仍能被创建:

public class WrapperClass implements Serializable {
    private void readObject(ObjectInputStream input) throws Exception {
        input.defaultReadObject();
        try {
            input.readObject();  // 读取可能抛出异常的对象
        } catch (Exception e) {
            // 捕获异常但继续执行
        }
    }
}

序列化结构:

TC_OBJECT - WrapperClass
    TC_OBJECT - 非法对象 (分配handle)
TC_REFERENCE - 引用非法对象

2.2.3 插入假成员

可以在TC_CLASSDESC中添加类中不存在的成员:

  • 反序列化时会为假成员分配handle
  • 实际对象不会包含这些成员
  • 可用于创建需要的引用关系

3. JRE8u20 Payload分析

3.1 核心组件

  1. BeanContextSupport类

    • 类似WrapperClass,提供try/catch保护
    • 用于安全地创建AnnotationInvocationHandler对象
  2. 假成员技术

    • 在HashSet的TC_CLASSDESC中添加假属性
    • 属性值为BeanContextSupport对象

3.2 执行流程

  1. 反序列化开始时创建HashSet
  2. 读取假成员时创建BeanContextSupport对象
  3. BeanContextSupport内部读取AnnotationInvocationHandler
    • 虽然会抛出异常但被捕获
    • AnnotationInvocationHandler对象已被创建并获得handle
  4. 后续payload通过TC_REFERENCE引用该对象
  5. 继续Jdk7u21的触发流程

3.3 代码实现关键点

// 创建AnnotationInvocationHandler
TCObject handler = new TCObject(ser) {
    @Override
    public void doWrite(DataOutputStream out, HandleContainer handles) throws Exception {
        // 特殊处理,去掉TC_ENDBLOCKDATA
        // 因为异常会导致序列化过程不能正常结束
    }
};

// 创建BeanContextSupport包装器
TCObject makeBeanContextSupport(TCObject handler, Serialization ser) {
    // 设置serializable成员
    // 添加handler作为子对象
    // 设置循环引用
}

// 主payload构造
TCObject linkedHashset = new TCObject(ser);
// 添加假成员
hashsetDesc.addField(new TCClassDesc.Field("fake", BeanContextSupport.class));
hashsetData.addData(makeBeanContextSupport(handler, ser));
// 添加其他必要数据

4. 防御与检测

4.1 防御措施

  1. 升级JDK到最新版本
  2. 使用安全管理器限制反序列化
  3. 替换默认的ObjectInputStream实现
  4. 使用白名单控制可反序列化的类

4.2 检测方法

  1. 监控异常日志中的"Non-annotation type in annotation serial stream"
  2. 检查序列化数据中的可疑结构:
    • 异常的TC_CLASSDESC
    • 不匹配的类描述与实际数据
    • 可疑的引用关系

5. 总结

JRE8u20 Payload通过以下技术创新绕过限制:

  1. 利用序列化过程中对象先创建后验证的特性
  2. 通过包装类捕获异常保持反序列化流程
  3. 精心构造的引用关系维持对象可用性
  4. 假成员技术创建必要的对象引用

理解这些技术对于防御复杂的反序列化攻击至关重要,也为安全研究人员提供了分析高级payload的方法论。

Java反序列化漏洞深入分析:JRE8u20 Payload详解 1. 背景知识 1.1 Jdk7u21 Payload回顾 Jdk7u21 Payload是一个经典的Java反序列化漏洞利用链,其核心原理如下: TemplatesImpl类 :可序列化,内部 __bytecodes 成员可存储class字节码 执行流程 : 通过 TemplatesImpl.getOutputProperties() 方法触发 __bytecodes 存储的字节码会被转换为Class对象(通过 ClassLoader.defineClass ) 实例化该Class,执行构造方法中的代码 触发机制 :利用 LinkedHashSet 与 AnnotationInvocationHandler 触发 getOutputProperties 1.2 Jdk7u21的修补 在7u21之后的版本中, AnnotationInvocationHandler.readObject 方法增加了检查: 关键点: 检查 this.type 是否为注解类型,非注解类型则抛出异常 异常抛出前已经通过 defaultReadObject() 还原了对象 这使得 this.type 必须为 Templates.class 的7u21 Payload失效 2. 绕过思路 2.1 核心发现 AnnotationInvocationHandler.readObject 的执行顺序: 先通过 var1.defaultReadObject() 还原对象 然后检查 this.type 并可能抛出异常 这意味着: 对象已经被成功还原 异常抛出后对象仍然存在 可以通过引用机制在其他地方使用这个对象 2.2 关键技术点 2.2.1 Java序列化引用机制 Java序列化会为每个对象分配一个handle,重复对象会使用TC_ REFERENCE引用已序列化的对象。 示例: 序列化结构: 2.2.2 异常处理技巧 通过包装类捕获异常,使"非法"对象仍能被创建: 序列化结构: 2.2.3 插入假成员 可以在TC_ CLASSDESC中添加类中不存在的成员: 反序列化时会为假成员分配handle 实际对象不会包含这些成员 可用于创建需要的引用关系 3. JRE8u20 Payload分析 3.1 核心组件 BeanContextSupport类 : 类似WrapperClass,提供try/catch保护 用于安全地创建AnnotationInvocationHandler对象 假成员技术 : 在HashSet的TC_ CLASSDESC中添加假属性 属性值为BeanContextSupport对象 3.2 执行流程 反序列化开始时创建HashSet 读取假成员时创建BeanContextSupport对象 BeanContextSupport内部读取AnnotationInvocationHandler 虽然会抛出异常但被捕获 AnnotationInvocationHandler对象已被创建并获得handle 后续payload通过TC_ REFERENCE引用该对象 继续Jdk7u21的触发流程 3.3 代码实现关键点 4. 防御与检测 4.1 防御措施 升级JDK到最新版本 使用安全管理器限制反序列化 替换默认的ObjectInputStream实现 使用白名单控制可反序列化的类 4.2 检测方法 监控异常日志中的"Non-annotation type in annotation serial stream" 检查序列化数据中的可疑结构: 异常的TC_ CLASSDESC 不匹配的类描述与实际数据 可疑的引用关系 5. 总结 JRE8u20 Payload通过以下技术创新绕过限制: 利用序列化过程中对象先创建后验证的特性 通过包装类捕获异常保持反序列化流程 精心构造的引用关系维持对象可用性 假成员技术创建必要的对象引用 理解这些技术对于防御复杂的反序列化攻击至关重要,也为安全研究人员提供了分析高级payload的方法论。