Insecure Deserialization in Java


Serialization is the process of converting an object into a data format which can later be used to restore the object. People often serialize objects in order to store or to send them as part of communications. Deserialization is the reverse of that process – taking data structured in some format and rebuilding it into an object.

Deserializing partially or fully untrusted data might result in serious attack vectors.

The problem

Java’s deserialization implementation is inherently insecure. The problem is not unique to Java, either - similar (exploitable) features exist in Python, PHP and many other languages.

Firstly, it is possible to create structures which will never finish deserializing, leading to a denial of service.

Secondly, it is possible to leverage libraries to gain remote code execution.

Identifying the vulnerability

Serialized Java objects begin with “ac ed” when in hexadecimal format and “rO0” when they are base64-encoded.

To view a file’s contents in hexadecimal mode, you can use the command:

xxd file-name-here

Denial of service (DoS)

The HashSet called “root” in the following code sample has members that are recursively linked to each other. When deserializing this “root” object, the JVM will begin creating a recursive object graph. It will never complete, and consume CPU infinitely.

Set root = new HashSet();
Set s1 = root;
Set s2 = new HashSet();
for (int i = 0; i < 100; i++) {
  Set t1 = new HashSet();
  Set t2 = new HashSet();
  t1.add("foo"); // make it not equal to t2
  s1.add(t1);
  s1.add(t2);
  s2.add(t1);
  s2.add(t2);
  s1 = t1;
  s2 = t2;
}
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("malicious-object"));
oos.writeObject(root);
oos.close();

You can use jshell to execute the commands and generate the serialized object.

Remote Code Execution (RCE)

Gaining remote code execution can be a bit more tricky. RCE is attained by exploiting certain useful libraries present in the application, for example Apache Commons or Hibernate.

How it works

In the case of Apache Commons, a chain of Transformers are used, with the final transformer executing code.

A transformer is an Apache Commons class which gets an object as an input and returns a different object as an output. Transformers are typically used for type conversions or extracting data from an object.

Read here to learn more about how the exploit chain works.

Practical exploitation

Typically the ysoserial tool is used to generate a payload. It allows you to generate payloads for many different libraries and for different versions of those libraries.

There is a Burp Suite plugin which will help you identify which payload can be used for exploitation.

For a good writeup of a successful attack, read this article.

Defending

The best defense is to never deserialize untrusted input. If possible, this is the solution you should go with.

However, if that is completely impossible, then it’s a good idea to take a look at the notsoserial tool, which allows whitelisting and blacklisting classes which can be deserialized.

Conclusion

Hopefully it’s now painfully clear that deserializing untrusted input is generally a very bad idea. Any existing applications which deserialize untrusted input should ideally be modified not to do so.

Further reading

1) Understanding ysoserial’s CommonsCollections1: http://gursevkalra.blogspot.com/2016/01/ysoserial-commonscollections1-exploit.html

2) Different Apache Commons Gadgets and what they do: https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html

3) Another writeup using the CommonsCollections1 payload: https://www.synopsys.com/content/dam/synopsys/sig-assets/whitepapers/exploiting-the-java-deserialization-vulnerability.pdf

Heino Sass Hallik