一、Java序列化与反序列化简介
序列化是让Java对象脱离Java运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储。
Java序列化
是指把Java对象转换为字节序列的过程(便于保存在内存、文件、数据库中),ObjectOutputStream
类的writeObject()
方法可以实现序列化。
Java反序列化
是指把字节序列恢复为Java对象的过程,ObjectInputStream
类的readObject()
方法用于反序列化。
实现java.io.Serializable
接口才可被反序列化,而且所有属性必须是可序列化的(用transient关键字修饰的属性除外,不参与序列化过程)
在Java中,只要一个类实现了java.io.Serializable
接口,那么它就可以通过ObjectInputStream与ObejctOutputStream序列化.
一个类的对象要想序列化成功,必须满足两个条件:
1.该类必须实现 java.io.Serializable 接口。
2.该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。
如果想知道一个 Java 标准类是否是可序列化的,可以通过查看该类的文档,查看该类有没有实现 java.io.Serializable
接口。
二、漏洞成因
序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
漏洞代码示例:
1 | ...... |
暴露或间接暴露反序列化API,导致用户可以操作传入数据,攻击者可以精心构造反序列化对象并执行恶意代码
两个或多个看似安全的模块在同一运行环境下,共同产生的安全问题。
触发场景
- 1.HTTP请求中的参数
- 2.RMI,即Java远程方法调用,在RMI中传输的数据皆为序列化
- 3.JMX,一个为应用程序植入管理功能的框架
- 4.自定义协议,用来接收与发送原始的java对象
相关工具: https://github.com/frohoff/ysoserial/
三、漏洞基本原理
JAVA序列化数据流特征
加密便于传输,不加密不容易阅读传输
数据流以
rO0AB
开端是JAVA序列化的base64加密aced
开端是JAVA序列化的16禁止加密
涉及到以下函数,则考虑JAVA反序列化:
1
2
3
4
5
6
7ObjectInputStream.readobject
ObjectInputStream.readUnshared
XMLDecoder.readObject
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject
.....
序列化
通过查看序列化后的数据,可以看到反序列化数据开头包含两字节的魔术数字,这两个字节始终为十六进制的0xAC ED。接下来是两字节的版本号0x00 05的数据。此外还包含了类名、成员变量的类型和个数等。
例子:SerialObject 示例
对象所属类:
1 | public class SerialObject implements Serializable{ |
序列化SerialObject实例后以二进制格式查看:
1 | 00000000: aced 0005 7372 0024 636f 6d2e 7878 7878 ....sr.$com.xxxx |
序列化的数据流以魔术数字和版本号开头,这个值是在调用ObjectOutputStream序列化时,由writeStreamHeader方法写入:
1 | protected void writeStreamHeader() throws IOException { |
反序列化
Java程序中类ObjectInputStream
的readObject
方法被用来将数据流反序列化为对象,如果流中的对象是class
,则它的ObjectStreamClass
描述符会被读取,并返回相应的class
对象,ObjectStreamClass
包含了类的名称及serialVersionUID
。
如果类描述符是动态代理类,则调用resolveProxyClass
方法来获取本地类。如果不是动态代理类则调用resolveClass
方法来获取本地类。如果无法解析该类,则抛出ClassNotFoundException
异常。
如果反序列化对象不是String、array、enum
类型,ObjectStreamClass
包含的类会在本地被检索,如果这个本地类没有实现java.io.Serializable
或者externalizable
接口,则抛出InvalidClassException
异常。因为只有实现了Serializable
和Externalizable
接口的类的对象才能被序列化。
readObject()
方法在反序列化漏洞中它起到了关键作用,readObject()方法被重写的的话,反序列化该类时调用便是重写后的readObject()方法。如果该方法书写不当的话就有可能引发恶意代码的执行.
四、检测Java反序列化漏洞
代码审计
重点关注一些反序列化操作函数并判断输入是否可控
基本思路:
1、通过检索源码中对反序列化函数的调用来静态寻找反序列化的输入点
可以搜索以下函数:
1 | ObjectInputStream.readObject |
小数点前面是类名,后面是方法名
2、确定了反序列化输入点后,再考察应用的Class Path中是否包含Apache Commons Collections等危险库(ysoserial所支持的其他库亦可)
3、若不包含危险库,则查看一些涉及命令、代码执行的代码区域,防止程序员代码不严谨,导致bug
4、若包含危险库,则使用ysoserial进行攻击复现
https://github.com/frohoff/ysoserial/
白盒审计
大型企业的应用很多,每个都人工去审计不现实,往往都有相应的自动化静态代码审计工具,这里以ObjectInputStream.readObject()为例,其它原理也相似。在自动化检测时,可通过实现解析java源代码,检测readObject()方法调用时判断其对象是否为java.io.ObjectOutputStream。如果此时ObjectInputStream对象的初始化参数来自外部请求输入参数则基本可以确定存在反序列化漏洞了。这是只需确认是否存在相应的安全修复即可。 检测方式可参考lgtm.com对于Deserialization of user-controlled data的实现
黑盒审计
调用ysoserial并依次生成各个第三方库的利用payload(也可以先分析依赖第三方包量,调用最多的几个库的paylaod即可),该payload构造为访问特定url链接的payload,根据http访问请求记录判断反序列化漏洞是否利用成功。如:
1 | java -jar ysoserial.jar CommonsCollections1 'curl " + URL + " ' |
也可通过DNS解析记录确定漏洞是否存在。现成的轮子很多,推荐NickstaDB写的SerialBrute,还有一个针对RMI的测试工具BaRMIe。
攻击检测
通过查看反序列化后的数据,可以看到反序列化数据开头包含两字节的魔术数字,这两个字节始终为十六进制的0xAC ED。接下来是两字节的版本号。我只见到过版本号为5(0x00 05)的数据。考虑到zip、base64各种编码,在攻击检测时可针对该特征进行匹配请求post中是否包含反序列化数据,判断是否为反序列化漏洞攻击。
1 | xxxdeMacBook-Pro:demo xxx$ xxd objectexp |
但仅从特征匹配只能确定有攻击尝试请求,还不能确定就存在反序列化漏洞,还要结合请求响应、返回内容等综合判断是否确实存在漏洞。
RASP检测
Java程序中类ObjectInputStream
的readObject
方法被用来将数据流反序列化为对象,如果流中的对象是class,则它的ObjectStreamClass描述符会被读取,并返回相应的class对象,ObjectStreamClass包含了类的名称及serialVersionUID。
类的名称及serialVersionUID的ObjectStreamClass描述符在序列化对象流的前面位置,且在readObject
反序列化时首先会调用resolveClass读取反序列化的类名,所以RASP检测反序列化漏洞时可通过重写ObjectInputStream对象的resolveClass方法获取反序列化的类即可实现对反序列化类的黑名单校验。
其他
1.从流量中发现序列化的痕迹,关键字:ac ed 00 05,rO0AB
2.Java RMI的传输100%基于反序列化,Java RMI的默认端口是1099端口
3.从源码入手,可以被序列化的类一定实现了Serializable接口
4.观察反序列化时的readObject()方法是否重写,重写中是否有设计不合理,可以被利用之处
从可控数据的反序列化或间接的反序列化接口入手,再在此基础上尝试构造序列化的对象。
ysoserial是一款非常好用的Java反序列化漏洞检测工具,该工具通过多种机制构造PoC,并灵活的运用了反射机制和动态代理机制,值得学习和研究。
五、防御Java反序列化漏洞
1.类白名单校验
在ObjectInputStream
中resolveClass 里只是进行了class 是否能被load,自定义ObjectInputStream, 重载resolveClass的方法,对className 进行白名单校验
1 | public final class test extends ObjectInputStream{ |
2.禁止JVM执行外部命令Runtime.exec
通过扩展SecurityManager可以实现:
1 | SecurityManager originalSecurityManager = System.getSecurityManager(); |
Java反序列化大多存在复杂系统间相互调用,控制,或较为底层的服务应用间交互等应用场景上,因此接口本身可能就存在一定的安全隐患。Java反序列化本身没有错,而是面对不安全的数据时,缺乏相应的防范,导致了一些安全问题。并且不容忽视的是,也许某些Java服务没有直接使用存在漏洞的Java库,但只要Lib中存在存在漏洞的Java库,依然可能会受到威胁。
六、修复Java反序列化漏洞
通过Hook resolveClass来校验反序列化的类
在使用readObject()
反序列化时首先会调用resolveClass方法读取反序列化的类名,所以这里通过重写ObjectInputStream
对象的resolveClass方法即可实现对反序列化类的校验。
具体实现代码Demo如下:
1 | public class AntObjectInputStream extends ObjectInputStream{ |
通过此方法,可灵活的设置允许反序列化类的白名单,也可设置不允许反序列化类的黑名单。但反序列化漏洞利用方法一直在不断的被发现,黑名单需要一直更新维护,且未公开的利用方法无法覆盖。
1 | org.apache.commons.collections.functors.InvokerTransformer |
根据以上方法,有大牛实现了线程的SerialKiller包可供使用。
使用ValidatingObjectInputStream来校验反序列化的类
使用Apache Commons IO Serialization包中的ValidatingObjectInputStream类的accept方法来实现反序列化类白/黑名单控制,具体可参考ValidatingObjectInputStream介绍;示例代码如下:
1 | private static Object deserialize(byte[] buffer) throws IOException, |
使用contrast-rO0防御反序列化攻击
contrast-rO0是一个轻量级的agent程序,通过通过重写ObjectInputStream来防御反序列化漏洞攻击。使用其中的SafeObjectInputStream类来实现反序列化类白/黑名单控制,示例代码如下:
1 | SafeObjectInputStream in = new SafeObjectInputStream(inputStream, true); |
使用ObjectInputFilter来校验反序列化的类
Java 9包含了支持序列化数据过滤的新特性,开发人员也可以继承java.io.ObjectInputFilter类重写checkInput方法实现自定义的过滤器,,并使用ObjectInputStream对象的setObjectInputFilter设置过滤器来实现反序列化类白/黑名单控制。示例代码如下:
1 | import java.util.List; |
上述示例代码,仅允许反序列化SerialObject类对象。
禁止JVM执行外部命令Runtime.exec
通过扩展SecurityManager
1 | SecurityManager originalSecurityManager = System.getSecurityManager(); |
不建议使用的黑名单
在反序列化时设置类的黑名单来防御反序列化漏洞利用及攻击,这个做法在源代码修复的时候并不是推荐的方法,因为你不能保证能覆盖所有可能的类,而且有新的利用payload出来时也需要随之更新黑名单,但有一种场景下可能黑名单是一个不错的选择。写代码的时候总会把一些经常用到的方法封装到公共类,这样其它工程中用到只需要导入jar包即可,此前已经见到很多提供反序列化操作的公共接口,使用第三方库反序列化接口就不好用白名单的方式来修复了。这个时候作为第三方库也不知道谁会调用接口,会反序列化什么类,所以这个时候可以使用黑名单的方式来禁止一些已知危险的类被反序列化,具体的黑名单类可参考contrast-rO0、ysoserial中paylaod包含的类。
Reference
http://www.bjpowernode.com/javazixun/9595.html
https://www.vulbox.com/knowledge/detail/?id=11
https://xz.aliyun.com/t/2043#toc-2
https://xz.aliyun.com/t/2041#toc-4