Featured image of post Java反序列化

Java反序列化

java反序列化基础

代码实现

 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 安全学习历程,也算是半条学习路线了)

最后更新于 Mar 03, 2025 07:35 UTC
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计