1.前言
Java序列化及反序列化处理在基于Java架构的Web应用中具有尤为重要的作用。例如位于网络两端、彼此不共享内存信息的两个Web应用在进行远程通信时,无论相互间发送何种类型的数据,在网络中实际上都是以二进制序列的形式传输的。为此,发送方必须将要发送的Java对象序列化为字节流,接收方则需要将字节流再反序列化,还原得到Java对象,才能实现正常通信。当攻击者输入精心构造的字节流被反序列化为恶意对象时,就会造成一系列的安全问题
反序列化基础
在Java原生的API中,序列化的过程由ObjectOutputStream
类的writeObject
方法实现,反序列化过程由Objectinputstream
类的readObject
方法实现。将字节流还原成对象的过程都可以称作反序列化,例如,JSON串或XML串还原成对象的过程也是反序列化的过程。同理,将对象转化成JSON串或XML串的过程也是序列化的过程。
能够被序列化的类必须要实现Serializable
接口或者Extemalizable
接口 ,Serializable
接口是一个标记接口,其中不包含任何方法。Extemalizable
接口是Serializable
子类,其中包含writeExtemal
和readExtemal
方法,分别在序列化和反序列化的时候自动调用。开发者可以在这两个方法中添加一些操作,以便在反序列化和序列化的过程中完成一些特殊的功能
Java反序列化通过ObjectInputStream类
的readObject
方法实现。在反序列化的过程中,一个字节流将按照二进制结构被序列化成一个对象。当开发者重写readobject
方法或readExtemal
方法时,其中如果隐藏有一些危险的操作且未对正在进行序列化的字节流进行充分的检测时,则会成为反序列化漏洞的触发点
漏洞产生的条件
原因在于服务端会反序列化
客户端传递的代码,这就会给予攻击者在服务器上运行代码的能力
- 入口类的readObject直接调用危险方法 (没有,不可能出现)
- 入口类参数中包含可控类,该类有危险方法
- 入口类参数中包含可控类,该类又调用其他有危险方法的类
- 构造函数/静态代码等类加载时隐式执行 (JAVA自身类加载也会执行一些代码)
入口类sink
:继承Serializable
接口,重写readobject
,例如Map Hashmap
中重写readobject
gadget chain
调用链:非常繁琐,根据相同名称,相同类型来寻找
执行类source
:(最重要)这是能够造成危害的代码执行点
反序列化过程是一个正常的业务需求,将正常的字节流还原成对象属于正常的功能。但是当程序中的某处触发点在还原对象的过程中,能够成功地执行构造出来的利用链,则会成为反序列化漏洞的触发点。反序列化的漏洞形成需要上述条件全部得到满足,程序中仅有一条利用链或者仅有一个反序列化的触发点都不会造成安全问题,不能被认定为漏洞
常见的反序列化项目:
• Ysoserial 原生序列化PoC生成
• Marshalsec 第三方格式序列化PoC生成
• Freddy burp反序列化测试插件
• Java-Deserialization-Cheat-Sheet