JEP290
0x01 What Is a JEP?
JDK Enhancement Proposal abbreviatedJEP, a project for JDK enhancement proposalsproject, whose index has now reachedJEP415. This article focuses on whatJEP290,JEP290does, andJEP290known bypass techniques, and related topics.
0x02 What Is JEP 290?
JEP290is described asFilter Incoming Serialization Data, meaning that it filters incoming serialized data
| F Clo 9 | core/io:serialization | 290 | Filter Incoming Serialization Data |
|---|
JEP290 is a Java filter designed to defend against deserialization attacks. It is proposal 290 in the JEP project and is commonly calledJEP290
0x03 Scope of JEP 290
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 What JEP 290 Does
- 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 an allowlist or denylist mechanism for restricting deserializable classes]
- Provide metrics to the filter for graph size and complexity during deserialization to validate normal graph behaviors. [Limit deserialization depth and complexity]
- Provide a mechanism for RMI-exported objects to validate the classes expected in invocations. [Provide class validation for remotely invoked RMI objects]
- The filter mechanism must not require subclassing or modification to existing subclasses of ObjectInputStream. [Define a configurable filter mechanism, for example through a properties file]
JEP 290 Details
### 1. Limits
- number of elements when deserializing a class array ( arrayLength )
- for each nested objectdepth( depth )
- current number of object references ( references )
- bytes consumed so far ( streamBytes )
### 2. Three Supported Filter-Configuration Methods
- custom filter
- process-wide filter (also called a global filter)
- built-in filters used by the RMI Registry and distributed garbage collection (DGC)
### 3. Custom Filters
A custom filter is appropriate when one deserialization operation has requirements different from the rest of the application. It can be created by implementingObjectInputFilterinterface and overridecheckInput(FilterInfo filterInfo)method to create a custom filter:
For example:
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;
}
}
}
inJDK 9 , Oracle added two methods to ObjectInputStream class gained two methods (getObjectInputFilter、setObjectInputFilter), allowing the currentObjectInputStreamset or retrieve a custom filter:
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;
}
...
}
Unlike JDK 9, the latest JDK 8 appears to permit this only onObjectInputFilter.Config.setObjectInputFilter(ois, new VehicleFilter());set a filter on it as follows:



### 4. Process-Wide (Global) Filter
You can configure a process-wide filter by settingjdk.serialFilteras a system or security property to configure the process-wide filter (in practice, by adding a command-line option when starting Java, such as:-Djdk.serialFilter=<allowlisted-class-1>;<allowlisted-class-2>;!<blocklisted-class>). If the system property is defined, it configures the filter; otherwise the security property is checked (JDK 8, 7, and 6: $JAVA_HOME/lib/security/java.security ; JDK 9 and later: $JAVA_HOME/conf/security/java.security) to configure a filter
You can also set it when starting the Java application-Djava.security.properties=<filter-config-file>
Specifically, by checking class names or limits on the incoming byte stream,jdk.serialFilterThe filter value is a semicolon-separated sequence of patterns. Each pattern matches either a class name in the stream or a limit; spaces are part of a pattern. Limits are checked before classes regardless of pattern order. The following limit properties are available:
-
maxdepth=value— maximum graph depth -
maxrefs=value— maximum number of internal references -
maxbytes=value— maximum bytes in the input stream -
maxarray=value— maximum permitted array sizeOther patterns match
Class.getName()class or package name returned by *.Class/PackagePatterns also accept asterisk (*), double asterisk (**), period (.) and forward slash (/) symbols. The following pattern scenarios are possible:JAVA// match specific classes and reject everything not listed "jdk.serialFilter=org.example.Vehicle;!*" // match classes in a package and all sub-packages, and reject everything not listed - "jdk.serialFilter=org.example.**;!*" // match all classes in a package and reject everything not listed - "jdk.serialFilter=org.example.*;!*" // match any class with the configured prefix - "jdk.serialFilter=*;5. Built-In Filters
Built-in filters are used for
RMI Registry, RMI distributed garbage collection (DGC), and Java Management Extensions (JMX)RMI Registryhas a built-in allowlist filter for objects bound to the registry. It covers the following cases:java.rmi.Remote- ``java.lang.Number`
java.lang.reflect.Proxyjava.rmi.server.UnicastRef- ``java.rmi.activation.ActivationId`
java.rmi.server.UID- ``java.rmi.server.RMIClientSocketFactory`
java.rmi.server.RMIServerSocketFactory
Built-in filters include size limits:
maxarray=1000000,maxdepth=20RMI distributed garbage collection has a built-in allowlist filter that accepts a limited set of classes. It covers the following cases:
java.rmi.server.ObjID- ``java.rmi.server.UID`
java.rmi.dgc.VMIDjava.rmi.dgc.Lease
Built-in filters include size limits:
maxarray=1000000,maxdepth=20In addition to these classes, users can use
sun.rmi.registry.registryFilter(forRMI Registry) andsun.rmi.transport.dgcFilter(for DGC) system or security property to add a custom filterFor
JMX filter, which can be used duringRMIServer.newClientremote calls and deserialized arguments sent to a server over RMI; the same method can also use thatmanagement.propertiesfile supplies the default agent with a filter-pattern string
0x05 Points to Note About JEP 290
-
JEP 290 must be configured manually. Filtering is active only after configuration; otherwise ordinary deserialization exploitation remains possible.
-
By default, JEP 290 provides built-in filters only for the RMI Registry layer, RMI distributed garbage collection (DGC), and JMX.
0x06 Bypassing JEP 290
Whether JEP 290 can be bypassed depends on whether a global filter is configured. Without one, exploitation may still occur at theapplicationlevel when no global filter exists. If a global filter is configured, exploitation requires discovering a new gadget chain.
-
A global filter is configured
- JDK7u21
- JDK8u20
- RMI Registry Bypass (2019; uses a new gadget chain)
-
No global filter is configured
- CVE-2018-4939 (2018) → Spring Framework RmiInvocationHandler, which can pass arbitrary objects to RemoteInvocation (using
an arbitrary object as a parameter) unmarshalValuemethod (fixed in JDK 8u242-b07, 11.0.6+10, 13.0.2+5, and 14.0.1+2 in January 2020; Java 9, 10, and 12 were not fixed)
- CVE-2018-4939 (2018) → Spring Framework RmiInvocationHandler, which can pass arbitrary objects to RemoteInvocation (using
0x07 Impact of JEP 290 Bypasses
- Previously unusable gadget chains may become exploitable again, including some Commons Collections chains.
- New gadget chains can be used
- WebLogic is heavily affected