Insecure Deserialization in Java
Concept of Serialization
The process of converting the state
of object into stream of bytes is called serialization.
The purpose of serialization is to save the object’s state to the file system or transmit it over the network for future use.
In the context of Java
Serializable
is amarker interface
.- It has no
data member
andmethod
.- It is only used to
mark
java classes so that objects of these type of classes may get a certaincapability
.
Create a User
class and make it serializable
.
Create an object from the User
class and save it into the file system in .ser
format.
Concept of Deserialization
The process of re-creating
the actual object in memory from byte stream is called de-serialization.
The
User
class must be available in the Java classpath for deserialization to succeed.
Observations
- For instance, if a serialized object is created using the
User
class but type checking during deserialization is performed with theSuperUser
class, then application will throw aClassCastException
.
- However, if a serialized object is created using the
SuperUser
class but type checking during deserialization is performed with theUser
class, the application will not throw any exception becauseSuperUser
class is derived from the base classUser
.
- Some objects may be required to implement
Serializable
due to inheritance for exampleSuperUser
. It inherites the base classUser
that implementsSerializable
.
To ensure that such objects (e.g., SuperUser
) cannot be deserialized, we can override the readObject()
method and mark it as final
to throw an exception during the deserialization process.
The Bug
The
readObject()
method ofjava.io.ObjectInputStream
is vulnerable.During the Deserialization process, the
readObject()
method is always being called, and it can construct any sort of Serializable object that can be found on the Java classpath before passing it back to the caller for the type check.An Exception occurs only when there’s a type mismatch between the returned object and the expected object. If the constructed object performs any harmful actions during its construction, it’s already too late to prevent them by the time type checking.
How to Identify
From a Blackbox perspective
Look for magic numbers like
AC ED 00 05
orrO0A
(base64-encoded) in the request/response to identify if the application is handling a serialized object.The
Content-Type
header in the HTTP response is set toapplication/x-java-serialized-object
.
⠀ From a Whitebox perspective
Search the codebase for Java Serialization APIs such as
ObjectInputStream
, particularly instances ofreadObject()
method, and analyze howObjectInputStream
is utilized.Before calling
readObject()
, ensure the code checks for all expected classes from the serialized object using awhitelist
.
What is the Impact
Remote code execution through
property-oriented programming
or gadget chaining.Bypass authorization or escalate privileges via Insecure Direct Object Reference (IDOR) if the object’s signature / authenticity is not verified.
Denial of Service (DoS) attacks, such as exhausting heap memory, CPU cycle.
How to Exploit
Perform Denial of Service
Generate a malicious serialized object using
DoSExploit.java
.During deserialization, when the application attempts to reconstruct the object in memory, it consumes 100% of the CPU resources.
Execute Remote Code
Leveraging ysoserial
- Generate the rce payload to open
gnome-calculator
using the latest ysoserial.
1
java -jar ysoserial-all.jar CommonsCollections7 gnome-calculator > bad_serialized_object_ysoserial.ser
- The modern Java Security Manager by default includes protections against unsafe deserialization by blocking blacklisted gadgets. Therefore,
disabling
that feature in code by adding the following line inDemoDeserilization.java
1
2
3
4
// in current Java, by default enableUnsafeSerialization is set to 'false'
System.setProperty(
"org.apache.commons.collections.enableUnsafeSerialization",
"true");
During deserialization, when the application tries to reconstruct the object in memory, it launches the calculator.
Handcraft the payload
I’ve put together a detailed blog post on how to create the entire RCE gadget chain from scratch.
- Set up the exploit dev environment
- JDK version:
openjdk-23
- Add
commons-collectios-3.2.2.jar
,commons-lang3-3.7.jar
andmockito-all-1.9.5.jar
into the Java classpath.
- JDK version:
- Download the exploit code - RCE.java.
Java’s strong encapsulation introduced in Java 9+, which restricts reflective access to certain internal Java classes and fields by default. This is especially relevant when using libraries or tools that attempt to access private or internal fields of classes like
HashMap
.
- By adding the
--add-opens
option, we can explicitly open the necessary package (java.util
) for reflection. In IntelliJ IDEA -> Run -> Edit Configurations -> In VM options field, add the following
1
--add-opens java.base/java.util=ALL-UNNAMED
- Execute
RCE.java
and generate therce_serialized_object
.
During deserialization, when the application attempts to reconstruct the object in memory, it opens the calculator.
How to Mitigate
Do not blindly accept serialized objects from untrusted sources. Implement integrity checks or sign the serialized objects to prevent tampering or the creation of malicious objects.
Use a whitelist approach to secure
java.io.ObjectInputStream
- Create a
HashSet
containing all expected classes wrapped in the object. - Extend
ObjectInputStream
to create a customSafeObjectInputStream
class. - Override the
resolveClass()
method to verify ifcls.getName()
exists in theHashSet
, otherwise, throw anInvalidClassException
.
- Create a
When we provide any object other than User
type, it throws exception.
When we provide User
type object, it does not throw any exception.
A Denial of Service (DoS) is inevitable if the
expected
object type is aHashSet
,HashMap
, orArrayList
.
Defense in depth
Use the
transient
keyword for sensitive fields that you do not want to be serialized. Thetransient
keyword prevents a variable, like a password field, from being serialized. When the JVM encounters a variable marked as transient orstatic
, it disregards its original value and instead saves the default value corresponding to that variable’s data type.For detective controls, log any exceptions or failures that occur during the deserialization process.
Use Java Security Manager to block specific classes such as
InvokerTransformer
.
1
2
3
4
// in current Java, by default enableUnsafeSerialization is set to 'false'
System.setProperty(
"org.apache.commons.collections.enableUnsafeSerialization",
"false");
Code Repo
https://github.com/greyshell/java_insecure_deserialization