反序列化

在Java中,想要使用Java原生的序列化框架,需要将待传输的对象所属的类继承与Java.io.Serializable接口。
在原生的Java反序列化过程中(writeObject),会产生一个简单描述(writeclassDesc方法),例如这个对象是哪个类创建的(out.wirteUTF),SUID字段(out.writeLong)以及是实现Serializable还是Externalizable接口(有一个flag字段来判断java序列化是使用哪种方式)。

SUID相当于一个序列化对象的指纹,随机生成,属于反序列化中比较重要的一个东西,类似身份证。
简单描述完毕后,开始写数据,调用writeOrdinaryObject,存在writeSerialData方法来开始生成序列化的数据。

子类继承了Serialiable接口,父类没继承Serialiable接口,此时传输序列化数据进行反序列化,父类会丢失。

Java序列化实现的方式有三种:
1、自定义实现Serialiable接口,使用原生的readobject和writeobject
2、自定义实现Serialiable接口,同时实现readobject和writeobject方法。
场景的话就是默认的反序列化可能不满足需求,比如在反序列化时还原数组的时候可能是无序的,此时你想让它还原的时候按照某些格式进行排列,比如hashmap之类的东西,此时就需要一个反序列化readobject方法的自定义实现。
3、自定义类实现Externalizable接口
Externalizable是Serialiable的子类,想要用这种序列化方式,需要自己实现writeExternal和readExternal方法,完全自定义序列化过程,不依赖原生Java

提取反序列化的数据
SerializationDumper
https://github.com/NickstaDB/SerializationDumper

反序列化漏洞的本质

加入一个被序列化的对象继承了Serialiable接口,并且重写了readObject和writeObject方法,那么他就会自动调用相关类的readObject方法,这个类就被我们叫做Gadget。

条件:

1、使用ObejectInputStream#readObject,且流可控,位置不限,只要有这个方法,且可控就行。
例如审计的代码存在一个可控的readObject代码段(条件1)
image.png
2、存在一个恶意类(傻缺类,这个类写了readobject方法,还有一些危险操作)
例如存在这个傻缺类,重写了readobject方法(条件2)
Java简单反序列化漏洞小结 - 图2
具体不一定是反序列化命令执行,也有可能是反序列化写入文件,反序列化调用反射

Gadget

一般情况下,傻缺类的不存在的,最常见的就是Apache CommonCollection

代码审计找的点

ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load XStream.fromXML
ObjectMapper.readValue
JSON.parseObject

修复:

在使用反序列化从流还原成对象时,写一个自己的反序列化类,添加黑名单,将ObjectInputSteam替换成自己写的,或者使用JEP290——官方缓解反序列化漏洞

JEP290

原本为JDK9加入现在在如下版本也加入了
JDK 8u121
JDK 7u131
JDK 6u141
JEP290机制是用来过滤传入的序列化数据,以提高安全性,在反序列化的过程中,新增了一个filterCheck方法,所以,任何反序列化操作都会经过这个filterCheck方法,利用checkInput方法来对序列化数据进行检测,如果有任何不合格的检测,Filter将返回REJECTED。
但是filterCheck方法需要手动设置,否则默认配置黑名单只包含部分反序列化链,也可以设置自己的白名单。

其他反序列学习
https://github.com/SummerSec/JavaLearnVulnerability