What is deserialization in Java?
Serialization in Java represents a process in which an object in the Java programming language is converted into a format that can either be transferred over a network or saved to a database. Deserialization in Java represents a process opposite to that. In the process of deserialization, a serialized Java object is read from a file or network and is being converted into an object.
The process of deserialization is supported in a lot of other programming languages, such as PHP, Python, and Ruby, just to name a few.
In the case of insecure Java deserialization, an attacker manipulates a serialized Java object with the goal to cause unintended consequences in the program flow, possibly causing DoS, remote code execution (RCE) or authentication bypass.
To learn more about deserialization in general, read our guide on deserialization.
In this article:
- Serialization interface in Java
- Exploiting Java insecure deserialization
- Preventing Java insecure deserialization bugs
Serialization interface in Java
Before we can exploit deserialization vulnerabilities in Java, we need to understand how serialization and deserialization work in Java, so let’s start with that.
The serialization of Java classes is enabled by implementing java.io.Serializable. For a Java application to handle serialization and deserialization of objects of a certain class, special methods need to be implemented to classes writeObject() and readObject(). If we don’t implement this interface to any class, objects of that class will not be serialized or deserialized.
Exploiting Java insecure deserialization
To exploit a Java application using an insecure deserialization bug, we first have to find an entry point to insert the malicious serialized object.
Serialized objects in Java are often used to transport data in HTTP headers, parameters, or cookies.
The Java serialized object
To recognize potential entry points for your exploit, look out for signatures all Java serialized objects have:
- The signature starts with AC ED 00 05 in Hex, or ro0 in Base64 (for example, you can find them within HTTP requests as cookies or parameters)
- Content-type header of an HTTP response set to application/x-java-serialized-object.
A lot of special characters can be found in Java serialized objects, so it is not uncommon for them to be encoded before transmission. Take a moment to look out for differently encoded versions of these signatures.
Manipulating object data and application logic
After you discover a user-supplied serialized object, try to manipulate the program logic by tampering with the data stored in the object. Try to change the usernames, role names, or other identity markers in the object, in case the Java object is used as a cookie for access control. After you do that, re-serialize the object and send it back to the application.
To test if you can manipulate the program’s flow, tamper with any sort of value in an object that is a file specifier or a file path, and control the flow values.
From insecure deserialization to code execution
With no restrictions in the application on what classes are allowed to get deserialized, all serializable classes that the current classloader can load can also be deserialized. That means that a user can create arbitrary classes! A potential attacker can use this to achieve Remote Code Execution (RCE) by constructing objects of the right classes that can lead to arbitrary commands.
The process of achieving remote code execution can be complicated, and there are a series of tools that need to be used to reach the desired method for code execution.
These tools and gadgets can be found in the libraries that a Java application loads. Try chaining method invocations that will eventually lead to remote code execution.
While creating the chain, ensure that the first gadget in the chain is self-executing. Look also for gadgets that are found in commonly available libraries to maximize the chances that your gadgets are in-scope for the application.
There are exploits out there developed and published using gadgets in some popular libraries like the Spring Framework, Groovy, Commons-Collection and Apache Commons Fileupload.
Limitations of this approach
This approach doesn’t come without limitations however. Finding and chaining gadgets to formulate an exploit is very time consuming. Keep in mind that you are limited to the classes that are available to the application, so you are limited in what you can do with the exploit. In addition, gadget classes have to implement serializable or externalizable, and different library versions may come with different usable gadgets.
Preventing Java insecure deserialization bugs
To prevent Java insecure deserialization bugs, make sure not to deserialize any data contaminated by user-input, without properly checking it. If you can’t avoid deserialization, restrict it to a small list of whitelisted classes.
Don’t use objects when you don’t have to – if possible, use simple data types like strings or arrays.
Don’t rely on user input for session information. Instead, keep the session state on the server. That will prevent the tampering of cookies.
Another important tip – keep an eye out for patches and keep the dependencies up to date.
How not to prevent Java insecure deserialization
Some developers try to prevent deserialization vulnerabilities by identifying commonly vulnerable classes and removing them from the application. This is, for sure, an effective way to restrict available gadgets! Why shouldn’t you do it then? Limiting gadgets is not a cure-all for deserialization issues.
Address the root cause of this vulnerability – insecure deserialization.
Hackers can always find more gadgets in other libraries, and come up with creative ways to achieve the same result.
Sign-up for a free Bright account and start testing for deserialization vulnerabilities today.