Skip to content

Serializable

date
2023-09-10 12:08:13

序列化与反序列化

序列化是将对象转换成数据字节流,反序列化时将数据字节流转换为对象。

序列化的是为了进程之间对象的传递。发送方将对象序列化为字节流,接收方从字节序列中恢复出对象完成传递。

在 Java 中,要对一个对象进行序列化,其必须实现 Serializable 接口。

接口 Serializable 用来标识当前类可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列化。

import java.io.Serializable;

// Person 类实现 Serializable 接口, 空接口, 不需要实现方法
public class Person implements Serializable {
    public String name;
    public int age;
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void print(){
        System.out.println("name:"+ name);
        System.out.println("age:"+ age);
    }
}

序列化

import java.io.*;

public class SerializeDemo {
    public static void main(String[] args) {
        Person person = new Person("fuming",18);
        try {
            // 创建一个输出流来写入文件
            FileOutputStream fileOut = new FileOutputStream("person.ser");
            // 创建一个对象输出流
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            // 序列化对象
            out.writeObject(person);
            // 关闭流
            out.close();
            fileOut.close();
            System.out.println("对象已经被序列化并保存在 person.ser 文件中");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这里将序列化的对象写入文件流中,.ser 用来表示这是一个序列化文件。

反序列化

创建一个输入流,读取序列化文件,然后将字节流反序列化为对象。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializeDemo {
    public static void main(String[] args) {
        try {
            // 创建一个输入流来读取文件
            FileInputStream fileIn = new FileInputStream("person.ser");
            // 创建一个对象输入流
            ObjectInputStream in = new ObjectInputStream(fileIn);
            // 反序列化对象
            Person person = (Person) in.readObject();
            // 关闭流
            in.close();
            fileIn.close();
            // 输出反序列化后的对象
            person.print();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

漏洞原理

Java 反序列化漏洞跟 PHP 其实是一样的,或者说反序列化漏洞的成因都是一样的:应用程序对攻击者可控的数据进行反序列化操作。

漏洞检测

黑盒测试可以通过 Java 反序列化特征自己或字符串:

  • Base64 :rO0AB
  • 16 进制:0xac ed 00 05

白盒测试可直接搜索 readObject 方法,再看其输入是否可控。

漏洞利用

Java 反序列化漏洞的利用往往需要组合多个不同的 Serializable 接口实现类的方法调用,形成复杂的调用链。

在 Java 安全领域,习惯称之为反序列化利用链,也叫 Gadget Chain

前人已经挖掘出了很多的利用链,他们对 Java 标准库以及各种常见的第三方库组件做了大量研究,最终找到了许多可以实现远程代码执行的反序列化利用链,这些利用链以及集成到了著名的 Java 反序列化利用工具 ysoserial 中。

项目地址:https://github.com/frohoff/ysoserial