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
Serializableis amarker interface.- It has no
data memberandmethod.- It is only used to
markjava 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
Userclass must be available in the Java classpath for deserialization to succeed.
Observations
- Case 1: If a serialized object is created using the
Userclass but during deserialization when thetype checkingis performed with theSuperUserclass, then application throws aClassCastException.
- Case 2: If a serialized object is created using the
SuperUserclass but during deserialization when thetype checkingis performed with theUserclass, then the application does not throw any exception becauseSuperUserclass is derived from the base classUser.
- Some objects may be required to implement
Serializabledue toinheritancefor exampleSuperUserclass. It inherits the base classUserthat 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.ObjectInputStreamis 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 05orrO0A(base64-encoded) in the request/response to identify if the application is handling a serialized object.The
Content-Typeheader 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 howObjectInputStreamis 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 programmingor 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-calculatorusing 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,
disablingthat 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.jarandmockito-all-1.9.5.jarinto 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-opensoption, 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.javaand 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
HashSetcontaining all expected classes wrapped in the object. - Extend
ObjectInputStreamto create a customSafeObjectInputStreamclass. - Override the
resolveClass()method to verify ifcls.getName()exists in theHashSet, otherwise, throw anInvalidClassException.
- Create a
For example,
- When we provide any object other than
Usertype, it throws exception.
- When we provide expected
Usertype object, it does not throw any exception.
A Denial of Service (DoS) is
inevitableif theexpectedobject type is aHashSet,HashMap, orArrayList.
Defense in depth
Use the
transientkeyword for sensitive fields that you do not want to be serialized. Thetransientkeyword 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");












