Insecure deserialization in Java is one of the most dangerous vulnerabilities in modern web applications. It has been responsible for multiple Remote Code Execution (RCE) attacks, large-scale data breaches, and critical enterprise compromises.

If you are a developer, security engineer, or penetration tester, understanding Java deserialization vulnerabilities is not optional. It is essential.

What is Deserialization in Java?

Serialization in Java is the process of converting an object into a byte stream so it can be:

  • Saved to a file
  • Sent over a network
  • Stored in a session
  • Cached in a database

Deserialization is the reverse process — converting the byte stream back into a Java object.

Java provides built-in support through:

  • ObjectOutputStream
  • ObjectInputStream
  • The Serializable interface

Simple Serialization Example:

import java.io.*; 
 
class User implements Serializable { 
    private static final long serialVersionUID = 1L; 
    String username; 
 
    public User(String username) { 
        this.username = username; 
    } 
} 
 
public class SerializeExample { 
    public static void main(String[] args) throws Exception { 
        User user = new User("admin"); 
 
        FileOutputStream fileOut = new FileOutputStream("user.ser"); 
        ObjectOutputStream out = new ObjectOutputStream(fileOut); 
        out.writeObject(user); 
        out.close(); 
        fileOut.close(); 
    } 
}

Deserialization Example:

import java.io.*; 
 
public class DeserializeExample { 
    public static void main(String[] args) throws Exception { 
        FileInputStream fileIn = new FileInputStream("user.ser"); 
        ObjectInputStream in = new ObjectInputStream(fileIn); 
        User user = (User) in.readObject(); 
        in.close(); 
        fileIn.close(); 
 
        System.out.println(user.username); 
    } 
}

Looks harmless. But here is where things go wrong.

What is Insecure Deserialization in Java?

Insecure deserialization occurs when untrusted data is deserialized without proper validation or filtering.

When a Java application deserializes attacker-controlled input, the attacker can:

  • Execute arbitrary code
  • Modify application logic
  • Access sensitive files
  • Escalate privileges
  • Achieve Remote Code Execution

This happens because Java deserialization can trigger method execution during object reconstruction.

Why is Java Deserialization Dangerous?

During deserialization, Java:

  • Reconstructs object state
  • Invokes constructors indirectly
  • Executes readObject() methods
  • Executes readResolve() methods
  • Executes readExternal() if implemented

If a class in the classpath contains dangerous code inside these methods, attackers can trigger it by crafting a malicious serialized payload.

This is where gadget chains come into play.

How Insecure Deserialization Leads to RCE

The typical attack flow:

  1. Application accepts serialized data from user input
  2. Attacker crafts a malicious serialized payload
  3. Application deserializes payload
  4. Gadget chain triggers
  5. Arbitrary command execution occurs

Vulnerable Java Code Example

import java.io.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 
 
public class VulnerableServlet extends HttpServlet { 
 
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException { 
 
        ObjectInputStream ois = new ObjectInputStream(request.getInputStream()); 
        try { 
            Object obj = ois.readObject(); 
            response.getWriter().println("Object received: " + obj); 
        } catch (ClassNotFoundException e) { 
            e.printStackTrace(); 
        } 
    } 
}

Problem:

  • It reads directly from user input.
  • No validation.
  • No class filtering.
  • No integrity checks.

An attacker can send a malicious serialized object that executes system commands.

Real Exploitation with ysoserial

Attackers commonly use tools like ysoserial to generate exploit payloads.

Example payload generation:

java -jar ysoserial.jar CommonsCollections1 "calc.exe" > payload.ser

Then send the payload to the vulnerable endpoint. If the server is vulnerable and the required library is present, it executes the command.

Understanding Gadget Chains

A gadget chain is a sequence of method calls that leads to code execution during deserialization.

Common libraries used in gadget chains:

  • Apache Commons Collections
  • Spring Framework
  • Groovy
  • Hibernate

Attackers do not need to inject new code. They reuse existing classes inside your application.

Dangerous Magic Methods in Java

Certain methods are automatically triggered during deserialization:

private void readObject(ObjectInputStream in) 
private Object readResolve() 
private void readExternal(ObjectInput in)

If these contain dangerous logic, attackers can abuse them. 

Example: 

private void readObject(ObjectInputStream in)  
        throws IOException, ClassNotFoundException { 
 
    in.defaultReadObject(); 
    Runtime.getRuntime().exec("calc.exe"); 
}

If this class exists in the classpath, an attacker can trigger command execution.

Real World Incidents

Major enterprise products affected by insecure Java deserialization:

  • WebLogic Server
  • JBoss
  • Jenkins
  • Apache Struts
  • Spring applications

Many critical CVEs involved Java deserialization flaws leading to full system compromise.

Understanding these exploitation techniques separates beginner developers from serious security professionals.

How to Detect Insecure Deserialization

1. Code Review

Search for:

ObjectInputStream 
readObject() 
readResolve()

If input is coming from:

  • HTTP requests
  • Cookies
  • Files
  • Network sockets

It is a red flag.

2. Penetration Testing

Look for:

  • Base64 encoded serialized objects
  • Session cookies containing serialized data
  • .ser file uploads

3. Automated Tools

  • Burp Suite
  • Static analysis tools
  • Dependency scanners

Secure Coding Practices to Prevent Insecure Deserialization

1. Avoid Native Java Serialization

Prefer:

  • JSON
  • XML
  • Protocol Buffers

Instead of:

  • ObjectInputStream
  • Serializable

Example using Jackson:

ObjectMapper mapper = new ObjectMapper(); 
User user = mapper.readValue(jsonInput, User.class);

JSON parsing does not execute arbitrary methods like Java deserialization.

2. Implement ObjectInputFilter (Java 9+)

Java 9 introduced deserialization filters.

Example:

ObjectInputFilter filter = ObjectInputFilter.Config.createFilter("com.myapp.*;!*"); 
ObjectInputStream ois = new ObjectInputStream(inputStream); 
ois.setObjectInputFilter(filter);

This restricts which classes can be deserialized.

3. Validate Input Before Deserialization

  • Verify digital signatures
  • Use HMAC validation
  • Validate integrity before processing

4. Remove Unnecessary Libraries

Many gadget chains depend on:

  • Apache Commons Collections
  • Legacy libraries

Remove unused dependencies to reduce attack surface.

5. Use Look-Ahead ObjectInputStream

Custom implementation to whitelist classes:

class SafeObjectInputStream extends ObjectInputStream { 
 
    public SafeObjectInputStream(InputStream in) throws IOException { 
        super(in); 
    } 
 
    @Override 
    protected Class<?> resolveClass(ObjectStreamClass desc) 
            throws IOException, ClassNotFoundException { 
 
        if (!desc.getName().equals("com.myapp.User")) { 
            throw new InvalidClassException("Unauthorized deserialization", desc.getName()); 
        } 
        return super.resolveClass(desc); 
    } 
}

This blocks unknown classes from being deserialized.

Insecure Deserialization vs JSON Injection

Many developers believe switching to JSON solves everything.

It helps, but:

  • Unsafe polymorphic deserialization in Jackson can also be abused.
  • Default typing can lead to RCE.

So always disable default typing unless required.

Best Practices Checklist

  • Do not deserialize untrusted input
  • Use JSON instead of native serialization
  • Apply ObjectInputFilter
  • Remove vulnerable libraries
  • Keep dependencies updated
  • Implement integrity checks
  • Conduct regular security testing

Why This Vulnerability Still Exists

Reasons insecure deserialization remains common:

  • Legacy enterprise systems
  • Lack of secure coding knowledge
  • Blind trust in internal networks
  • Outdated libraries

Security is not theoretical. Attackers actively scan for deserialization endpoints.

Advanced Attack Techniques

Experienced attackers combine deserialization with:

  • SSRF
  • File upload bypass
  • XXE
  • Authentication bypass
  • JNDI injection

Deserialization is often just the entry point. Understanding chained exploitation is what makes elite penetration testers.

TL;DR

Insecure deserialization in Java is not just another vulnerability. It is a critical Remote Code Execution vector capable of full system compromise.

Key takeaways:

  • Never deserialize untrusted data
  • Use modern data formats like JSON
  • Apply strict class filtering
  • Remove unnecessary dependencies
  • Regularly test your applications

Security is a skill built through practice. If you want to go beyond theory and master real-world exploitation techniques, enroll in the Web Hacking Extreme Course today: https://academy.redfoxsec.com/course/web-hacking-extreme-course-102762

Build offensive skills. Strengthen defensive expertise. Become a security professional who truly understands Java deserialization attacks.