JDK Enhancement Proposal
简称JEP
,是 JDK 增强提议的一个项目,目前索引编号已经达到了JEP415,本文重点来谈谈什么是JEP290
,JEP290
做了哪些事,JEP290
绕过的方法总结等。
JEP290
的描述是Filter Incoming Serialization Data
,即过滤传入的序列化数据
F Clo 9 | core/io:serialization | 290 | Filter Incoming Serialization Data |
---|
JEP290
是 Java 为了防御反序列化攻击而设置的一种过滤器,其在 JEP 项目中编号为290,因而通常被简称为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)
当反序列化要求与整个应用程序中的任何其他反序列化过程不同时,就会出现自定义过滤器的配置场景;可以通过实现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
类里添加了两个方法(getObjectInputFilter
、setObjectInputFilter
),允许为当前的 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());
上设置过滤器,如下所示:
可以通过将jdk.serialFilter设置为系统属性或安全属性来配置进程范围的过滤器(其实就是在启动Java应用时添加命令行参数,如:-Djdk.serialFilter=<白名单类1>;<白名单类2>;!<黑名单类>)。如果定义了系统属性,则用于配置过滤器;否则过滤器会检查安全属性(JDK 8、7、6:
此外,也可以在启动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
文件向默认代理提供过滤器模式字符串
对于JEP290的绕过其实要基于有没有配置全局过滤器,如果没有,那么有可能在应用程序级别中利用反序列化漏洞,但如果配置了全局过滤器,那么只能通过发现新的gadget链去利用。
任意对象作为参数
)unmarshalValue
方法(2020年1月在JDK 8u242-b07、 11.0.6+10、13.0.2+5、14.0.1+2中修复,Java 版本 9、10 和 12 未修复)