The Fundamentals of JEP 290

SummaryJEP 290 0x01 What Is a JEP? JDK Enhancement Proposal, abbreviated JEP, is the process for proposing JDK enhancements. The index has reached JEP 415. This article focuses on JEP 290: what it is, what it does, and known bypass approaches. 0x02 What Is JEP 290? JEP 290 is described as Filter Incoming…

javaJava deserializationSerializationJEP290

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 whatJEP290JEP290does, andJEP290known bypass techniques, and related topics.[1][2]

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

### 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:

JAVA
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 (getObjectInputFiltersetObjectInputFilter), allowing the currentObjectInputStreamset or retrieve a custom filter:

JAVA
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:

1.png
2.png
3.png

### 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 size

    Other patterns matchClass.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 forRMI Registry, RMI distributed garbage collection (DGC), and Java Management Extensions (JMX)

    RMI Registry has 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.Proxy
    • java.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=20

    RMI 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.VMID
    • java.rmi.dgc.Lease

    Built-in filters include size limits:

    maxarray=1000000,maxdepth=20

    In addition to these classes, users can usesun.rmi.registry.registryFilter(forRMI Registry) andsun.rmi.transport.dgcFilter(for DGC) system or security property to add a custom filter

    ForJMX 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

  • No global filter is configured

    • CVE-2018-4939 (2018) → Spring Framework RmiInvocationHandler, which can pass arbitrary objects to RemoteInvocation (usingan 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)

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