关于Java序列化和挑选化的讨论
#java #applicationsecurity

前言

可以说,当前的Java安全性是红色团队的必不可少的。我曾经陷入学习Java安全的开始 - 学习CC链 - 放弃 - 再次开始学习Java,安全的安全循环就像记住单词,并且总是停止放弃。最后,在看到了高级SCZ的博客之后,我下定决心。

基本用途

不再引入序列化和挑选化的概念,让我们直接看一下用户。

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

public interface Serializable {  
}

它仅用于识别该类可以被序列化,后者继承了前者。

Java对象的序列化步骤:

  • 创建一个可以包裹另一个TypeJava.io.ObjectOutputStream

  • 的输出流的创建一个
  • writeObjectWrite对象通过其方法

避免java对象的步骤:

  • 创建一个可以包裹另一个类型的输入流 java.io.objectInputStream

  • readObject 通过其方法读取一个对象

序列化数据格式

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

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

使用该工具: serializationDumper 序列化数据很容易恢复,例如原始流量文件:

对于十六进制数据:

完成输入:

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

第一位是魔术号码和协议版本。您可以在ObjectStreamConstantsInterface:

中查看定义

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

对象内部的内容是序列化数据的核心,包括以下任何一个:

  • newObject:object

  • newClass:Kind

  • newarray:array

  • newstring:字符串

  • newenum:枚举类型

  • newClassDesc:类定义

  • prevobject:引用类型

  • nullReference:null

  • 例外:brormal

  • tc_reset:reset ReferentionId

请参阅新对象:

我们使用上面的示例进行比较,其中包含一个newObject的内容,tc_objectafter是classDesc,其中包含类名称和长度,serialversionuid,属性名称和长度,父级和其他信息。之后是newhandle,前者是序列化数据中当前结构的唯一ID,后者是序列化对象中的信息。classData[]

newclassdescand classDESC并不相同,从定义可以看出:

classdescequivalent to newclassdescthe封装,可以newClassDescbe null或指向类定义的指针。

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

您需要了解有关序列化数据结构的更多原因的三个原因:

  • 通过在序列化数据中填充垃圾字符

  • ,有助于理解绕过WAF的原理
  • 有助于了解JDK 8U20稍后

  • 也许您会遇到需要使用其他语言来进行Java Delelialization exploits

  • 的情况

属性的效果

在PHP中序列化时,变量的范围会影响序列化数据,因此Java中是否存在类似的情况?

向人群添加两个变量:

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

由静态或瞬态关键字修改的变量不会出现在序列化数据中,这是针对某些敏感数据注意事项的。

但是,如果您尝试在避免后调用这两个变量,则可以看到地址正态输出,而密码为null:

这是因为解决了一个静态变量,其在JVM中注册的值,而不是序列化后获得的值。

如果要通过瞬态关键字修改序列化变量,则需要使用externalizable Interface:

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

的原因是,也必须通过相应的readexternalMethod来解析,该类实现了外部化接口的类,其序列化将通过该方法写入流,因此无法在不提供原始类的情况下解析序列化数据。

ObjectStreamClass分析

ObjectStreamClass可以用于分析JVM中加载的序列化类的序列化特征,包括字段说明信息和serialversionuidetc。

ObjectStreamClassThere是两个静态方法:

查找(class <?> cl)objectStreamClass 如果提供的类是可序列化的,则返回实例,否则为null:

该方法将返回相应的实例,无论提供的类是必不可少的。 查找任何(class <?> cl)

获得 objectStreamClass 实例后,您可以调用相应的方法获取信息:

  • getDeclaredSuid :提取序列号

  • geterialfields :提取所需的序列化字段,如果不是,请提取默认字段

关于ObjectInputStream.Resolveclass()

image.png

resolveclass 该方法接收 objectStreamClass 实例,获得其类名称,然后使用反射返回此类的 class 实例。实际上,它可以在返回对象之前替换或解析对象。

Apache Shiro中的此方法已覆盖:

这会导致一些有趣的情况,当时Shiro值得启发,这将在后来的Shiro文章中进行详细讨论。

覆盖此方法也是一种防御应力化漏洞的一种手段,例如Serialkiller [3]项目:

黑名单或白名单防御是通过重写.objectInputStream.Resolveclass()

执行的

ReadObjectNodata

在避难化期间,如果序列化类的超类与避难类的超级类别不同,因为序列化时的类别与应对序列化时的版本不同,或者因为接收到的序列化数据不完整,则或序列化数据是有害的,将影响初始化的对象字段值。

因此,可序列化类应定义其自己的 readbigntnodata 方法,该方法将用于 read> read> readboctnodata ReadObject 。没有此方法,类的字段将初始化为其默认值。

例如,使用当前类序列化:

更新此类,并使用先前序列化的数据进行验证:

将被调用。 ReadObjectNodata() readobject()

概括

这是“ Java安全指南”中的第一篇文章。有些地方实际上有些奇怪,它不一定对Java安全学习有所帮助,但是了解更多,对:)

始终是正确的。

参考