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.

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

0x08 References

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