Java反序列化

Bet体育365验证提款 2026-07-11 06:31:51 admin

代码实现

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

package serialize;

import java.io.Serializable;

public class Person implements Serializable {

private String name;

private int age;

public Person(){

}

// 构造函数

public Person(String name, int age){

this.name = name;

this.age = age;

}

@Override

public String toString(){

return "Person{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

package serialize;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectOutput;

import java.io.ObjectOutputStream;

public class Serialization {

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("李二",22);

// System.out.println(person);

serialize(person);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package serialize;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

public class Unserialization {

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));

Object obj = ois.readObject();

return obj;

}

public static void main(String[] args) throws Exception {

Person person = (Person) unserialize("ser.bin");

System.out.println(person);

}

}

Person.java

Serializable 接口在 Java 中用于实现对象的序列化和反序列化。序列化是将对象转换为字节流,方便传输或存储,反序列化是将字节流还原为对象,如果这里删掉,下面的序列化操作会报错

Serialization.java

这里是将序列化操作封装在一个方法体里, 将对象序列化成字节流并写入文件 ser.bin

Unserialization.java

读入ser.bin的数据并将字节流反序列化为对象

运行后

(1) 序列化类的属性没有实现 Serializable那么在序列化就会报错

只有实现 了Serializable或者 Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)

Serializable 接口是 Java 提供的序列化接口,它是一个空接口,所以其实我们不需要实现什么。

1

2

public interface Serializable {

}

Serializable 用来标识当前类可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列化。如果我们此处将 Serializable 接口删除掉的话,会导致如下结果。

(2) 在反序列化过程中,它的父类如果没有实现序列化接口,那么将需要提供无参构造函数来重新创建对象。

(3)一个实现 Serializable接口的子类也是可以被序列化的。

(4) 静态成员变量是不能被序列化

序列化是针对对象属性的,而静态成员变量是属于类的。

(5) transient 标识的对象成员变量不参与序列化

利用反序列化

在序列化的时候,需要用到writeObject(Object obj) 方法来 将对象序列化为字节流并写入 ObjectOutputStream 中

在反序列化的时候,需要用到readObject()方法 从 ObjectInputStream 中读取字节流,并将其反序列化为一个对象

而readObject()可以被重写,从而执行我们想要的代码

(1)入口类的直接调用危险方法

Person.java添加代码

运行序列化,再运行反序列化

这就相当于重写readObject方法,从而弹计算器

其他的利用都是通过链子穿起来的

(2) 入口参数中包含可控类,该类有危险方法,readObject时调用

(3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,时调用

(4) 构造函数/静态代码块等类加载时隐式执行

产生漏洞的攻击路线

首先的攻击前提:继承 Serializable

入口类:source (重写 readObject 调用常见的函数;参数类型宽泛,比如可以传入一个类作为参数;最好 jdk 自带)

找到入口类之后要找调用链 gadget chain 相同名称、相同类型

执行类 sink (RCE SSRF 写文件等等)比如exec这种函数

参考博客

[大佬的博客](Drun1baby/JavaSecurityLearning: 记录一下 Java 安全学习历程,也算是半条学习路线了)