JEP290的基本概念

本文最后更新于 2021.06.25,总计 7449 字 ,阅读本文大概需要 8 ~ 28 分钟
本文已超过 1256天 没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

JEP290

0x01 什么是JEP?

JDK Enhancement Proposal 简称JEP,是 JDK 增强提议的一个项目,目前索引编号已经达到了JEP415,本文重点来谈谈什么是JEP290JEP290做了哪些事,JEP290绕过的方法总结等。

0x02 什么是JEP290?

JEP290的描述是Filter Incoming Serialization Data,即过滤传入的序列化数据

F Clo 9core/io:serialization290Filter Incoming Serialization Data

JEP290 是 Java 为了防御反序列化攻击而设置的一种过滤器,其在 JEP 项目中编号为290,因而通常被简称为JEP290

0x03 JEP290的适用范围

Java™ SE Development Kit 8, Update 121 (JDK 8u121)

Java™ SE Development Kit 7, Update 131 (JDK 7u131)

Java™ SE Development Kit 6, Update 141 (JDK 6u141)

0x04 JEP290 的作用

  • Provide a flexible mechanism to narrow the classes that can be deserialized from any class available to an application down to a context-appropriate set of classes. [提供一个限制反序列化类的机制,白名单或者黑名单]
  • Provide metrics to the filter for graph size and complexity during deserialization to validate normal graph behaviors. [限制反序列化的深度和复杂度]
  • Provide a mechanism for RMI-exported objects to validate the classes expected in invocations. [ 为RMI远程调用对象提供了一个验证类的机制]
  • The filter mechanism must not require subclassing or modification to existing subclasses of ObjectInputStream. [定义一个可配置的过滤机制,比如可以通过配置 properties文件的形式来定义过滤器]

JEP290 具体内容

1、限制的情况:

2、支持 3 种配置过滤器的方式

  • 自定义过滤器
  • 进程范围过滤器(也称为全局过滤器)
  • 用于 RMI 注册表和分布式垃圾收集 (DGC)使用的内置过滤器

3、自定义过滤器

当反序列化要求与整个应用程序中的任何其他反序列化过程不同时,就会出现自定义过滤器的配置场景;可以通过实现ObjectInputFilter接口并覆盖checkInput(FilterInfo filterInfo)方法来创建自定义过滤器:

如以下示例:

static class VehicleFilter implements ObjectInputFilter {
        final Class<?> clazz = Vehicle.class;
        final long arrayLength = -1L;
        final long totalObjectRefs = 1L;
        final long depth = 1l;
        final long streamBytes = 95L;

        public Status checkInput(FilterInfo filterInfo) {
            if (filterInfo.arrayLength() < this.arrayLength || filterInfo.arrayLength() > this.arrayLength
                    || filterInfo.references() < this.totalObjectRefs || filterInfo.references() > this.totalObjectRefs
                    || filterInfo.depth() < this.depth || filterInfo.depth() > this.depth || filterInfo.streamBytes() < this.streamBytes
                    || filterInfo.streamBytes() > this.streamBytes) {
                return Status.REJECTED;
            }

            if (filterInfo.serialClass() == null) {
                return Status.UNDECIDED;
            }

            if (filterInfo.serialClass() != null && filterInfo.serialClass() == this.clazz) {
                return Status.ALLOWED;
            } else {
                return Status.REJECTED;
            }
        }
    }

JDK 9 中,oracle 向 ObjectInputStream 类里添加了两个方法(getObjectInputFiltersetObjectInputFilter),允许为当前的 ObjectInputStream 设置或者获取自定义的过滤器:

public class ObjectInputStream
    extends InputStream implements ObjectInput, ObjectStreamConstants {

    private ObjectInputFilter serialFilter;
    ...
    public final ObjectInputFilter getObjectInputFilter() {
        return serialFilter;
    }

    public final void setObjectInputFilter(ObjectInputFilter filter) {
        ...
        this.serialFilter = filter;
    }
    ...
} 

与 JDK 9 不同,最新的 JDK 8 似乎只允许在ObjectInputFilter.Config.setObjectInputFilter(ois, new VehicleFilter());上设置过滤器,如下所示:

1.png

2.png

3.png

4、进程范围(全局)过滤器

可以通过将jdk.serialFilter设置为系统属性或安全属性来配置进程范围的过滤器(其实就是在启动Java应用时添加命令行参数,如:-Djdk.serialFilter=<白名单类1>;<白名单类2>;!<黑名单类>)。如果定义了系统属性,则用于配置过滤器;否则过滤器会检查安全属性(JDK 8、7、6: $JAVA_HOME/lib/security/java.security ;JDK 9 及更高版本: $JAVA_HOME/conf/security/java.security)以配置过滤器

此外,也可以在启动Java应用时设置-Djava.security.properties=<黑白名单配置文件名>

具体来说,通过检查类名或传入字节流属性的限制,jdk.serialFilter的值被过滤器被配置为一系列模式,每个模式要么与流中类的名称匹配,要么与限制匹配。模式由分号分隔,空格也被认为是模式的一部分。无论模式序列的配置顺序如何,都会在类之前检查限制。以下是可在配置期间使用的限制属性:

  • maxdepth=value — 图的最大深度
  • maxrefs=value — 内部参考的最大数量
  • maxbytes=value — 输入流中的最大字节数
  • maxarray=value — 允许的最大数组大小

其他模式与Class.getName()返回的类或包名称匹配*。Class/Package模式也接受星号 (*)、双星号 (**)、句点 (.) 和正斜杠 (/) 符号。以下是可能发生的几种模式场景:

//匹配特定的类并拒绝非列表中的类
"jdk.serialFilter=org.example.Vehicle;!*" 

 //匹配包和所有子包中的类并拒绝非列表中的类
- "jdk.serialFilter=org.example.**;!*" 

// 匹配包中的所有类并拒绝非列表中的类
- "jdk.serialFilter=org.example.*;!*" 

 // 匹配任何以设置样式为前缀的类
- "jdk.serialFilter=*;

### 5、内置过滤器

内置过滤器用于 RMI Registry 、RMI 分布式垃圾收集器(DCG)和 Java 管理扩展(JMX)

RMI Registry 有一个内置的白名单过滤器,允许将对象绑定到注册表中。它包括的情况如下:

  • java.rmi.Remote
  • `java.lang.Number
  • java.lang.reflect.Proxy
  • java.rmi.server.UnicastRef
  • `java.rmi.activation.ActivationId
  • java.rmi.server.UID
  • `java.rmi.server.RMIClientSocketFactory
  • java.rmi.server.RMIServerSocketFactory

内置过滤器包括大小限制:

maxarray=1000000,maxdepth=20

RMI 分布式垃圾收集器有一个内置的白名单过滤器,它接受一组有限的类。它包括的情况如下:

  • java.rmi.server.ObjID
  • `java.rmi.server.UID
  • java.rmi.dgc.VMID
  • java.rmi.dgc.Lease

内置过滤器包括大小限制:

maxarray=1000000,maxdepth=20

除了这些类之外,用户还可以使用sun.rmi.registry.registryFilter(针对RMI Registry)和sun.rmi.transport.dgcFilter(针对DGC)系统或安全属性添加自己的自定义过滤器

对于JMX 过滤器,可以在进行RMIServer.newClient远程调用以及通过 RMI 向服务器发送反序列化参数时,指定要使用的反序列化过滤器模式字符串;还可以使用该management.properties文件向默认代理提供过滤器模式字符串

0x05 JEP290 值得注意的点

  • JEP290需要手动设置,只有设置了之后才会有过滤,没有设置的话就还是可以正常的反序列化漏洞利用
  • JEP290默认只为 RMI 注册表(RMI Register层)、 RMI分布式垃圾收集器(DGC层)以及 JMX 提供了相应的内置过滤器

0x06 JEP290的绕过

对于JEP290的绕过其实要基于有没有配置全局过滤器,如果没有,那么有可能在应用程序级别中利用反序列化漏洞,但如果配置了全局过滤器,那么只能通过发现新的gadget链去利用。

  • 配置了全局过滤器

  • 未配置全局过滤器

    • CVE-2018-4939 (2018) --> Spring Framework RmiInvocationHandler,它可以将任意对象传递给RemoteInvocation类(利用的是任意对象作为参数
    • unmarshalValue方法(2020年1月在JDK 8u242-b07、 11.0.6+10、13.0.2+5、14.0.1+2中修复,Java 版本 9、10 和 12 未修复)

0x07 JEP290 绕过带来的影响

  • 以前不能用的gadget链可能被再次利用。[如一些 CC 链可能被利用]
  • 新的gadget链能够被使用
  • WebLogic深受影响

0x08 参考

https://access.redhat.com/blogs/766093/posts/3135411

https://docs.oracle.com/javase/10/core/serialization-filtering1.htm#JSCOR-GUID-3ECB288D-E5BD-4412-892F-E9BB11D4C98A

「感谢老板送来的软糖/蛋糕/布丁/牛奶/冰阔乐!」

panda

(๑>ڡ<)☆谢谢老板~

使用微信扫描二维码打赏

版权属于:

Panda | 热爱安全的理想少年

本文链接:

https://cnpanda.net/sec/968.html(转载时请注明本文出处及文章链接)

暂时无法评论哦~

暂无评论