Are EAP 7.0, and 7.1 really vulnerable to CVE-2017-7525, and CVE-2017-15095

Solution Verified - Updated -

Environment

  • Red Hat JBoss EAP (Enterprise Application Platform) 7.0
  • Red Hat JBoss EAP (Enterprise Application Platform) 7.1

Issue

How is EAP 7.0, and 7.1 vulnerable to the Jackson Databind issue reported in CVE-2017-7525 and CVE-2017-15095?

Resolution

While there was a patch released for CVE-2017-7525 in JBoss EAP 7.0 CP7, and EAP 7.1 those products are not using the library in a vulnerable way. There are patches for CVE-2017-15090 scheduled to be fixed in EAP 7.1 CP1.
Because it's still possible to get access to the affected class ObjectMapper, and use it in a vulnerable way we decided that patching was good idea. This article's main purpose is to describe the scenarios which could make an application deployed to JBoss EAP 7 vulnerable. By default JBoss EAP 7.x is not vulnerable to this issue.
CVE-2017-15095 was raised because of an incomplete fix for CVE-2017-7525, therefore they have the same root cause. The incomplete fix was a result of the way the issue was patched. The patch for CVE-2017-7525 added a blacklist to jackson-databind so that some 'gadget-chain' classes could not be unmarshalled. The term 'gadget-chain' is used to refer to classes which allow arbitrary code to be run when it's unmarshalled. However later it was found that the blacklist was incomplete, so further classes were added to the blacklist.

Root Cause

The jackson-databind library has a vulnerability which can be exploited if polymorphic unmarshalling is enabled, see 1. The jackson-databind library is included in EAP 7 to support JSON marshalling for JAX-RS. RESTEasy is the JAX-RS implementation on EAP 7, and it uses jackson-databind for marshalling of Java types into JSON and unmarshalling them from JSON into Java.
Normally when developing a webservice with JAX-RS polymorphic unmarshalling is not necessary. This is because we know the type we expect to receive at our endpoint. In this case we don't have any need for polymorphic unmarshalling as our webservice is expecting a particular concrete type. For example:

@Path("/")
public class HelloWorld {

    @PUT
    @Path("/two")
    @Consumes({"application/json"})
    public void resourceMethodTwo(ProxyWithGenericReturnTypeJacksonType1 arg) {
        System.out.println("Class of argument" + arg.getClass());
    }

}

Where ProxyWithGenericReturnTypeJacksonType1 is a concrete type

It is possible to develop a JAX-RS webservice which enables default typing by implementing a javax.ws.rs.ext.Provider which provides access to the ObjectMapper, and sets it to enableDefaultTyping, or one of it's other methods which allows polymorphc unmarshalling from 2. For example:

   @Provider
   public static class TestMapperResolver implements ContextResolver<ObjectMapper> {

      public ObjectMapper getContext(Class<?> type) {
         ObjectMapper mapper = new TestMapper();
         mapper.enableDefaultTyping();
         return mapper;
      }
   }

Setting enableDefaultType on objectMapper is NOT safe

A more likely scenarios is to allow polymorphic unmarshalling for a limited set of classes, there is a testcase in the RESTEasy source code which demonstrates how it can be done, 3. When setting up a project with a webservice endpoint which can handle polymorphic unmarshalling such as this, we found that the types available are restricted to subtypes specified in @SubTypeInfo, or the parameter type.
Take for example this type hierarchy:

public abstract class ProxyWithGenericReturnTypeJacksonAbstractParent {

    protected long id;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
}

With 2 subtypes:

public class ProxyWithGenericReturnTypeJacksonType1 extends ProxyWithGenericReturnTypeJacksonAbstractParent {

    protected String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

and

public class ProxyWithGenericReturnTypeJacksonType2 extends ProxyWithGenericReturnTypeJacksonAbstractParent {

    protected String note;

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }
}

Using the example from the RESTEasy source code, we could tell Jackson how to handle a webservice request with an abstract type ProxyWithGenericReturnTypeJacksonAbstractParent by adding an annotation to the class like this:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = ProxyWithGenericReturnTypeJacksonType1.class, name = "type1"),
        @JsonSubTypes.Type(value = ProxyWithGenericReturnTypeJacksonType2.class, name = "type2")})
public abstract class ProxyWithGenericReturnTypeJacksonAbstractParent {
...
}

Now we can create a webservice which produces and consumes objects of the abstract type ProxyWithGenericReturnTypeJacksonAbstractParent:

@Path("/")
public class HelloWorld {

    @GET
    @Path("/one")
    @Produces({ "application/json" })
    public ProxyWithGenericReturnTypeJacksonAbstractParent resourceMethodOne() {
        ProxyWithGenericReturnTypeJacksonType1 first = new ProxyWithGenericReturnTypeJacksonType1();
        first.setId(1);
        first.setName("type1");
        return first;
    }

    @PUT
    @Path("/two")
    @Consumes({"application/json"})
    public void resourceMethodTwo(ProxyWithGenericReturnTypeJacksonAbstractParent arg) {
        System.out.println("Class of argument" + arg.getClass());
    }

}

With this configuration it's only possible to unmarshal objects of ProxyWithGenericReturnTypeJackson; Type1, or Type2. An attempt to unmarshal anything else results in an error . For example this request:

curl -H 'Content-Type: application/json' -X PUT -d '{"type":"type3","id":1,"name":"type1"}' http://localhost:8080/CVE-2017-15095/rest/two

Results in an expection being thrown:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id &#x27;type3&#x27; into a subtype of [simple type, class org.jboss.as.quickstarts.rshelloworld.types.ProxyWithGenericReturnTypeJacksonAbstractParent]: known type ids = [ProxyWithGenericReturnTypeJacksonAbstractParent, type1, type2]

There is an alternative way to use @JsonTypeInfo, using Id.CLASS however. For example:

import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")
public abstract class ProxyWithGenericReturnTypeJacksonAbstractParent {
...
}

In this case the parameter used in the webservice endpoint is used to determine the supertype to expect. For example, for a request where class is not in the type hierarchy:

curl -H 'Content-Type: application/json' -X PUT -d '{"class":"org.jboss.as.quickstarts.rshelloworld.HelloWorld","id":1,"name":"type1"}' http://localhost:8080/CVE-2017-15095/rest/two

An exception is through such as:

java.lang.IllegalArgumentException: Class org.jboss.as.quickstarts.rshelloworld.HelloWorld not subtype of [simple type, class org.jboss.as.quickstarts.rshelloworld.types.ProxyWithGenericReturnTypeJacksonAbstractParent]

There is one way we found that arbitrary classes could be loaded by RESTEasy. That is if java.lang.Object is used as the Parameter type. For example in webservice class:

    @PUT
    @Path("/two")
    @Consumes({"application/json"})
    public void resourceMethodTwo(Object arg) {
        System.out.println("Class of argument" + arg.getClass());
    }

If a JAX-RS webservice does specify Object as the parameter than it's likely it could be vulnerable to CVE-2017-7525 and CVE-2017-15095. Therefore it's recommended to specify a concrete type, or use @JsonTypeInfo with @JsonSubTypes when expecting an abstract class, or an interface.


  1. https://github.com/mbechler/marshalsec/raw/master/marshalsec.pdf ↩︎

  2. https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization ↩︎

  3. https://github.com/resteasy/Resteasy/blob/3.1.4.Final/testsuite/integration-tests/src/test/java/org/jboss/resteasy/test/providers/jackson2/resource/ProxyWithGenericReturnTypeJacksonAbstractParent.java ↩︎

Diagnostic Steps

  • Are you using JAX-RS in your application on JBoss EAP 7?
    • Search your codebase for usage of classes in the 'javax.ws.rs' namespace.
  • Are you using @JsonTypeInfo to enable polymorphic unmarshalling of any of your JAX-RS data types.
  • Are you using java.lang.Object are a parameter to any of your webservice methods?

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.

Comments