目前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:提取所需的序列化字段,如果没有,则提取默认字段
图片.png
解析类该方法接收一个ObjectStreamClass实例,获取其类名,然后使用反射返回此类的Class实例。实际上,允许在反序列化期间返回对象之前替换或解析对象。
此方法在 Apache Shiro 中被覆盖:
这导致了 Shiro 反序列化漏洞的一些有趣情况,这将在后面的 Shiro 文章中详细讨论。
重写此方法也是防御反序列化漏洞的一种方法,例如 SerialKiller [3] 项目:
黑名单或白名单防御是通过重写来执行的。ObjectInputStream.resolveClass()
反序列化过程中,如果序列化类的超类与反序列化类的超类不同,因为序列化时的类与反序列化时的版本不同,或者因为收到的序列化数据不完整,或者序列化数据有害,会影响初始化的对象字段值。