-

开启 java 安全学习第二站,java 序列化与反序列化

0x01 意义

我们都知道就算是一条内裤都有它的作用和意义,那么序列化和反序列化存在的意义又是什么呢?也就是说我们可以用这东西来干什么?

  • 实现两个进程间对象的传输

    这个就是 java 序列化和反序列化的定义了,前者将对象转化为字节流数据,后者将字节流数据还原为对象

  • 实现数据的持久化

    对象序列化之后可以把字节数据写入文件中(硬盘里),这样就可以减轻内存的负担,再用的时候反序列化还原即可。比如配置信息,用户的数据等等。

  • 规范传递格式,方便网络传输

    这个也很好理解,文本音频视频这些数据全被序列化为二进制数据了,当然很好传输。

0x02 序列化与反序列化的实现

序列化的实现

  1. 实现 Serializable 接口:将要序列化的类实现 Serializable 接口。
  2. 创建ObjectOutputStream:创建一个ObjectOutputStream对象,用于将对象序列化为字节流。
  3. 写入对象:使用writeObject()方法将对象写入到输出流中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Student implements Serializable {
private String name;
private int age;

public Student(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}
public int getAge() {
return age;
}
}

public class Serialization {
public static void main(String[] args) {
Student stu = new Student("she11F",18);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.bin"))){
oos.writeObject(stu);
}catch(IOException e){
e.printStackTrace();
}
}
}

这样把一个学生的实例序列化成了二进制数据,并且写入了 student.bin 文件中

image-20240711163037275

反序列化的实现

  1. 创建 ObjectInputStream:创建一个ObjectInputStream对象,用于从字节流中读取对象。
  2. 读取对象:使用 readObject() 方法从输入流中读取对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class DeSerialization {
public static void main(String[] args) {
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.bin"))){
Student stu = (Student) ois.readObject();
System.out.println(stu.getName());
System.out.println(stu.getAge());
}catch(Exception e){
e.printStackTrace();
}
}
}

image-20240711165429376

0x03 漏洞

jdk 提供重写 writeObject,readObject 的权限

1
2
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException

而且默认执行用户重写的,用户没有重写则调用系统的。那我们重写 readObject 夹带危险函数,那么服务器端反序列化的时候就有可能中招了

我们在 Student 类里重新定义 readObject

1
2
3
4
private void readObject(ObjectInputStream ois)throws java.io.IOException, ClassNotFoundException{
ois.defaultReadObject(); // 执行默认的反序列化
Runtime.getRuntime().exec("calc.exe"); // 危险代码
}

然后序列化这个类再反序列化就会弹计算器了,当然现实开发中不太可能暴漏这种接口,所以得学其他思想,我先去学了,后面再做补充