Java反序列化基础篇-01-反序列化概念与利用
Java反序列化基础篇-01-反序列化概念与利用
0x01 序列化与反序列化
1. 什么是序列化与反序列化
序列化:对象 -> 字符串
反序列化:字符串 -> 对象
2. 为什么我们需要序列化与反序列化
序列化与反序列化的设计就是用来传输数据的。
当两个进程进行通信的时候,可以通过序列化反序列化来进行传输。
序列化的好处
(1) 能够实现数据的持久化,通过序列化可以把数据永久的保存在硬盘上,也可以理解为通过序列化将数据保存在文件中。
(2) 利用序列化实现远程通信,在网络上传送对象的字节序列。
序列化与反序列化应用的场景
(1) 想把内存中的对象保存到一个文件中或者是数据库当中。
(2) 用套接字在网络上传输对象。
(3) 通过 RMI 传输对象的时候。
3. 几种创建的序列化和反序列化协议
XML&SOAP
JSON
Protobuf
0x02 序列化与反序列化代码实现
1. 代码展示
- 类文件:Person.java
package Ser_01; |
- 序列化文件 SerializationTest.java
package Ser_01; |
- 反序列化文件 UnserializeTest.java
package Ser_01; |
2. 序列化与反序列化的代码讲解
基本实现
- SerializationTest.java
这里我们将代码进行了封装,将序列化功能封装进了 serialize 这个方法里面,在序列化当中,我们通过这个 FileOutputStream
输出流对象,将序列化的对象输出到 ser.bin
当中。再调用 oos 的 writeObject
方法,将对象进行序列化操作。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); |
- UnserializeTest.java进行反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); |
Serializable 接口
(1) 序列化类的属性没有实现 Serializable 那么在序列化就会报错
只有实现 了Serializable 或者 Externalizable 接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
Serializable 接口是 Java 提供的序列化接口,它是一个空接口,所以其实我们不需要实现什么。
public interface Serializable { |
Serializable 用来标识当前类可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列化。如果我们此处将 Serializable 接口删除掉的话,会导致如下结果。
(2) 在反序列化过程中,它的父类如果没有实现序列化接口,那么将需要提供无参构造函数来重新创建对象。
(3)一个实现 Serializable 接口的子类也是可以被序列化的。
(4) 静态成员变量是不能被序列化
序列化是针对对象属性的,而静态成员变量是属于类的。
(5) transient 标识的对象成员变量不参与序列化
这里我们可以动手实操一下,将 Person.java 中的 name
加上 transient
的类型标识。加完之后再跑我们的序列化与反序列化的两个程序,修改过程与运行结果如图所示。
0x03 为什么会产生序列化的安全问题
1. 引子
- 序列化与反序列化当中有两个 “特别特别特别特别特别” 重要的方法 ————
writeObject
和readObject
。
这两个方法可以经过开发者重写,一般序列化的重写都是由于下面这种场景诞生的。
举个例子,MyList 这个类定义了一个 arr 数组属性,初始化的数组长度为 100。在实际序列化时如果让 arr 属性参与序列化的话,那么长度为 100 的数组都会被序列化下来,但是我在数组中可能只存放 30 个数组而已,这明显是不可理的,所以这里就要自定义序列化过程啦,具体的做法是重写以下两个 private 方法:
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException |
只要服务端反序列化数据,客户端传递类的 readObject
中代码会自动执行,基于攻击者在服务器上运行代码的能力。
所以从根本上来说,Java 反序列化的漏洞的与
readObject
有关。
2. 可能存在安全漏洞的形式
(1) 入口类的 readObject
直接调用危险方法
先运行序列化程序 ———— “SerializationTest.java“,再运行反序列化程序 ———— “UnserializeTest.java“
这时候就会弹出计算器,也就是 calc.exe
,是不是帅的飞起哈哈。
这是黑客最理想的情况,但是这种情况几乎不会出现。
(2) 入口参数中包含可控类,该类有危险方法,readObject
时调用
(3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject
时调用
(4) 构造函数/静态代码块等类加载时隐式执行
3. 产生漏洞的攻击路线
首先的攻击前提:继承 Serializable
入口类:source (重写 readObject 调用常见的函数;参数类型宽泛,比如可以传入一个类作为参数;最好 jdk 自带)
找到入口类之后要找调用链 gadget chain 相同名称、相同类型
执行类 sink (RCE SSRF 写文件等等)比如 exec
这种函数