一、JEP290简介
1.JEP290 JDK版本
JEP290机制在2016年被提出,本身为Java9的特性,官方决定向下引进该增强机制,分别对6、7、8进行支持:8u121,7u131,6u141。
2.JEP290包含的机制
(1)提供一个限制反序列化类的机制,白名单或者黑名单
(2)限制反序列化的深度和复杂度
(3)为RMI远程调用对象提供了一个验证类的机制
(4)定义一个可配置的过滤机制,比如可以通过配置properties文件的形式来定义过滤器
上面的这些是参考网上文章直接复制过来,乍一看模棱两可。
3.JEP290防御原理
(1)原始反序列化防御
JEP290之前,对放序列化的防御也都只是缓解,方法是写一个ObjectInputStream 的子类,这个子类要重写 resolveClass(),并在这个方法中验证一个类是否能被加载,也就是实现白名单和黑名单 。在白名单中,只能让可接受的类被反序列化解析,其他的类会被阻止。黑名单则是收集已知会造成问题的类,然后把它们全部阻止。
(2)JEP290防御
https://docs.oracle.com/javase/9/docs/api/java/io/*.html
核心原理是,反序列化过滤基于 java.io.ObjectInputFilter接口,这个接口提供一种配置能力,目的是在反序列化过程中验证输入的数据。通过ObjectInputFilter接口参数:Status.ALLOWED , Status.REJECTED或者 Status.UNDECIDED 去检查输入数据的状态。比如,如果想用黑名单的形式,那么遇到一些特殊的类就要返回Status.REJECTED,并且如果返回Status.UNDECIDED的话,就是允许反序列化。另外一方面,如果想用白名单的形式,那么当返回Status.ALLOWED的时候,就代表匹配到了白名单里面的类。
此外,过滤器也被允许访问一些反序列化数据中的一些其他信息,比如,在反序列化过程中类数组中数组的长度 arrayLength ,每一个内置对象的深度 depth,当前对象的引用数量 references ,当前二进制流占用空间的大小 streamBytes 。这些提供了关于输入流更多的细粒度信息,并且在每一次匹配中都会返回相应的状态。
4.JEP290配置过滤器方式
(1) process-wide filter 全局过滤器(进程级过滤器)
启动Java应用时添加命令行参数
-Djdk.serialFilter=<白名单类1>;<白名单类2>;!<黑名单类>
或者设置Java系统属性(Java 6,7,8)
$JAVA_HOME/jre/lib/security/java.security(官方的是错的)
或者启动Java应用时设置
-Djava.security.properties=<黑白名单配置文件名>
全局过滤器一旦被设置,将无法被绕过
(2) custom filter 用户自定义过滤器
可以覆盖进程级过滤器!
当程序员很明确知道他想要反序列化的类是在哪个具体的包下,或者具体的类。需要程序员手动修改应用程序级别的代码。
示例代码:
ObjectInputFilter filesOnlyFilter = ObjectInputFilter.Config.createFilter("de.mogwailabs.Example;!*");
(3) built-in filters 内置过滤器
专门用于RMI,现在习惯用 Distributed Garbage Collection (DGC)
jdk 9 也引进了一个内置的过滤器,配置这个过滤器主要用于RMI和DGC 。RMI Registry 和 DGC的内置过滤器是白名单的形式,白名单包含了服务器能够执行的类。
JEP290中对RMI设置了默认的过滤器(sun.rmi.registry.RegistryImpl#registryFilter)
JDK会判断反序列化的类以及父类是否在白名单中(仅用于RmiRegistry)
相对应的DGCImple中也有 sun.rmi.transport.DGCImpl#checkInput
可以看到在java.security中是有说明配置了的,用户如果想自定义可以选择取消ava.security注释添加自己自定义即可
PS:补充一个之前一直不清楚的小知识DGC
分布式辣鸡回收
在Java虚拟机中,对于一个本地对象,只要不被本地Java虚拟机中的任何变量引用,它就可以被垃圾回收器回收了。
而对于一个远程对象,不仅会被本地Java虚拟机中的变量引用还会被远程引用。如将远程对象注册到Rregistry时,Registry注册表就会持有它的远程引用。
RMI框架采用分布式垃圾收集机制(DGC,Distributed Garbage Collection)来管理远程对象的生命周期。DGC的主要规则是,只有当一个远程对象不受任何本地引用和远程引用,这个远程对象才会结束生命周期。当客户端获得了一个服务器端的远程对象存根时,就会向服务器发送一条租约通知,告诉服务器自己持有这个远程对象的引用了。此租约有一个租约期限,租约期限可通过系统属性java.rmi.dgc.leaseValue来设置,以毫秒为单位,其默认值为600 000毫秒。如果租约到期后服务器端没有继续收到客户端新的租约通知,服务器端就会认为这个客户已经不再持有远程对象的引用。
因此可以通过与DGC通信的方式发送恶意payload让服务端进行反序列化,从而执行任意命令。
二、参考链接
https://xz.aliyun.com/t/3210
http://openjdk.java.net/jeps/290
https://paper.seebug.org/1251/
https://xz.aliyun.com/t/8706
https://cert.360.cn/report/detail?id=add23f0eafd94923a1fa116a76dee0a1
情绪很多的时候说明你很闲
原文始发于微信公众号(赛博少女):简单复习下JEP290
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论