Java教程

关于 Java 序列化和反序列化的谈话

本文主要是介绍关于 Java 序列化和反序列化的谈话,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

目前Java安全的普及可以说是红队的必知之道。我曾经陷入了学习Java安全的初衷——学习CC链——放弃——再次开始学习Java的安全恶性循环就像背单词,总是止步于放弃。最后,在看到高级scz的博客后,我下定了决心。

基本用途

不再介绍序列化和反序列化的概念,让我们直接看一下用户。

对于可序列化的类,它需要实现可序列化外部化接口,前者是一个空接口:

public interface Serializable {  
}

它仅用于标识此类可以序列化,后者继承前者。

Java 对象的序列化步骤:

  • 创建一个可以包装另一个类型的输出流java.io.ObjectOutputStream

  • writeObject通过其方法编写对象

反序列化 Java 对象的步骤:

  • 创建一个可以包装另一种类型的输入流java.io.ObjectInputStream

  • 读取对象通过对象的方法读取对象

序列化数据格式

将序列化数据写入文件并观察:

您可以看到有一些可读的字符串,包括类名和一些成员变量名称和值。

使用该工具:序列化转储程序可以轻松还原序列化数据,例如原始流文件:

对于十六进制数据:

完整输入:

与 Oracle 官方文档中的对象序列化流协议 [2] 部分相比,让我们讨论以下序列化数据的结构。

首先是幻数和协议版本。您可以在 ObjectStreamConstants接口中看到定义:

后跟内容,即一个或多个内容,后者由反对者块数据组成。

对象里面的内容是序列化数据的核心,由以下任何内容组成:

  • 新对象:对象

  • 新类:种类

  • 新数组:数组

  • 新字符串:字符串

  • newEnum:枚举类型

  • newClassDesc:类定义

  • 上一页对象:对类型的引用

  • 空引用:空

  • 异常:异常

  • TC_RESET:重置引用 ID

请参阅新对象:

我们使用上面的例子来比较,包含一个 newObject 的内容,TC_OBJECTafter classDesc,它包含类名和长度、serialVersionUID、属性名称和长度、父类和其他信息。在此之后是 newHandle,前者是序列化数据中当前结构的唯一 ID,后者是序列化 object.classdata[] 中的信息。

newClassDescand classDesc并不相同,从定义中可以看出:

classDescEquivalent to newClassDescthe 封装,它可以是 newClassDescbe 的空值或指向类定义的指针。

在这一点上,你可能想知道,我为什么要阅读这个无聊的文档?你为什么不开始谈论CC链?

需要了解有关序列化数据结构的详细信息有三个原因:

  • 帮助理解通过填充序列化数据中的垃圾字符来绕过 WAF 的原理

  • 帮助以后了解 JDK 8u20 本机反序列化漏洞

  • 也许您会遇到需要使用其他语言来执行 Java 反序列化漏洞的情况

属性的效果

在 PHP 中序列化时,变量的作用域会影响序列化的数据,那么在 Java 中是否有类似的情况呢?

将两个变量添加到 Personclass 中:

观察序列化数据后,发现这两个变量都不存在:

由 static 或 transient 关键字修改的变量不会出现在序列化数据中,这是出于一些敏感数据的考虑。

但是如果你尝试在反序列化后调用这两个变量,你可以看到地址正常输出,而密码为空:

这是因为寻址一个静态变量,并且调用它在 JVM 中注册的值,而不是序列化后获得的值。

如果要序列化由瞬态关键字修改的变量,则需要使用 外部化接口:

在这里,如果用于解析,将发生以下错误:test.rawSerializationDumper

原因是实现 Externalizable 接口的类,其序列化是通过该方法写入流的 writeExternal,也必须通过相应的 readExternalMethod 进行解析,因此 SerializationDumper这样的序列化数据如果不提供原始类就无法解析。

对象流类分析

ObjectStreamClass 它可以用来分析JVM中加载的序列化类的序列化特征,包括字段描述信息和serialVersionUID等。

ObjectStreamClass有两种静态方法:

lookup(Class<?>cl) ObjectStreamClass如果提供的类是可序列化的,则返回一个实例,否则为 null:

该方法将返回相应的实例,而不管提供的类是否可反序列化。lookup any(Class<?>cl)

获取ObjectStreamClass实例后,可以调用相应的方法来获取信息:

  • getDeclaredSUID:提取序列号

  • getSerialFields:提取所需的序列化字段,如果没有,则提取默认字段

关于 ObjectInputStream.resolveClass()

图片.png

解析类该方法接收一个ObjectStreamClass实例,获取其类名,然后使用反射返回此类的Class实例。实际上,允许在反序列化期间返回对象之前替换或解析对象。

此方法在 Apache Shiro 中被覆盖:

这导致了 Shiro 反序列化漏洞的一些有趣情况,这将在后面的 Shiro 文章中详细讨论。

重写此方法也是防御反序列化漏洞的一种方法,例如 SerialKiller [3] 项目:

黑名单或白名单防御是通过重写来执行的。ObjectInputStream.resolveClass()

readObjectNoData

反序列化过程中,如果序列化类的超类与反序列化类的超类不同,因为序列化时的类与反序列化时的版本不同,或者因为收到的序列化数据不完整,或者序列化数据有害,会影响初始化的对象字段值。

这篇关于关于 Java 序列化和反序列化的谈话的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!