OWASP Web Top 10 — A08

Insecure Deserialization

Insecure Deserialization is a critical vulnerability that occurs when untrusted data is used to reconstruct objects in an application. It can lead to remote code execution, privilege escalation, and complete system compromise, making it one of the most dangerous web application security risks.

What Is Insecure Deserialization?

Insecure Deserialization is a vulnerability that arises when an application deserializes (reconstructs) objects from untrusted data sources without proper validation or sanitization. Serialization is the process of converting an object into a format that can be stored or transmitted (such as JSON, XML, or binary), while deserialization reverses this process to recreate the object. When attackers can manipulate serialized data before it is deserialized, they can inject malicious objects that execute arbitrary code or alter application logic.

This vulnerability was identified as A08 in the 2017 OWASP Top 10 as "Insecure Deserialization" due to its severe impact and increasing prevalence. In the 2021 OWASP Top 10, it was merged into the broader category A08: Software and Data Integrity Failures, which encompasses deserialization vulnerabilities along with other integrity-related issues such as insecure CI/CD pipelines and unsigned code execution. Despite this categorization change, insecure deserialization remains a critical threat that can lead to complete system takeover.

The danger of insecure deserialization lies in the fact that serialized data often contains not just simple values, but complex object graphs with code references, class definitions, and method invocations. When an attacker controls this data, they can craft malicious payloads that exploit the deserialization process itself. Many programming languages and frameworks provide native serialization mechanisms that, by design, can instantiate arbitrary classes and invoke methods during deserialization, creating a powerful attack surface if not properly secured.

How It Works

1
Attacker identifies deserialization endpoint

The attacker discovers an application endpoint that accepts serialized data, such as a session cookie, API parameter, or file upload. They analyze the data format to determine the serialization mechanism being used (Java serialization, Python pickle, PHP serialize, etc.).

2
Crafts malicious serialized object

Using knowledge of available classes in the application or its dependencies, the attacker constructs a "gadget chain"—a sequence of method calls that, when triggered during deserialization, execute malicious code. For example, in Java, attackers might leverage classes from Apache Commons Collections to build a payload that executes system commands.

3
Sends malicious payload to application

The attacker transmits the crafted serialized object to the vulnerable endpoint. This could be through modifying a session cookie, sending a POST request with serialized data, or uploading a malicious file that will be deserialized by the server.

4
Application deserializes without validation

The application receives the serialized data and deserializes it without performing integrity checks, type validation, or allowlist filtering. The deserialization process automatically instantiates objects and invokes methods defined in the serialized data, treating the untrusted input as legitimate.

5
Malicious code executes

As the deserialization process reconstructs the malicious object graph, the gadget chain triggers, executing the attacker's payload. This can result in remote code execution, allowing the attacker to run arbitrary commands on the server, access sensitive data, or completely compromise the system.

Vulnerable Code Example

Vulnerable — Java / Spring Boot
@RestController
public class UserPreferencesController {

    @PostMapping("/preferences/load")
    public ResponseEntity<?> loadPreferences(@RequestBody byte[] data) {
        try {
            // VULNERABLE: Deserializing untrusted data
            // without any validation or filtering
            ByteArrayInputStream bis = new ByteArrayInputStream(data);
            ObjectInputStream ois = new ObjectInputStream(bis);

            // This will execute any code embedded in the
            // serialized object during deserialization
            UserPreferences prefs = (UserPreferences) ois.readObject();

            return ResponseEntity.ok(prefs);
        } catch (Exception e) {
            return ResponseEntity.status(500).body("Error loading preferences");
        }
    }
}

// An attacker can craft a malicious serialized object using
// gadget chains from libraries like Apache Commons Collections.
// When deserialized, this object executes arbitrary commands:
//
// ysoserial CommonsCollections1 "rm -rf /tmp/*" | base64
//
// The resulting payload, when sent to /preferences/load,
// will execute the command during deserialization.

Secure Code Example

Secure — Java / Spring Boot (ObjectInputFilter + JSON)
@RestController
public class UserPreferencesController {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @PostMapping("/preferences/load")
    public ResponseEntity<?> loadPreferences(@RequestBody String jsonData) {
        try {
            // SECURE: Use JSON instead of Java serialization
            // JSON only supports data types, not arbitrary code
            UserPreferences prefs = objectMapper.readValue(
                jsonData, UserPreferences.class
            );

            return ResponseEntity.ok(prefs);
        } catch (Exception e) {
            return ResponseEntity.status(400).body("Invalid preferences format");
        }
    }

    // If you MUST use Java serialization, implement strict allowlisting:
    public Object secureDeserialize(byte[] data) throws Exception {
        ByteArrayInputStream bis = new ByteArrayInputStream(data);

        // Use ObjectInputFilter to allowlist only safe classes
        ObjectInputStream ois = new ObjectInputStream(bis);
        ois.setObjectInputFilter(info -> {
            if (info.serialClass() != null) {
                String className = info.serialClass().getName();
                // Only allow specific, safe classes
                if (className.equals("com.example.UserPreferences")) {
                    return ObjectInputFilter.Status.ALLOWED;
                }
                return ObjectInputFilter.Status.REJECTED;
            }
            return ObjectInputFilter.Status.UNDECIDED;
        });

        return ois.readObject();
    }
}

// Using JSON eliminates gadget chain attacks entirely.
// If native serialization is required, ObjectInputFilter
// restricts deserialization to explicitly allowed classes.

Types of Insecure Deserialization

Remote Code Execution (RCE)

The most severe form of insecure deserialization, where attackers exploit gadget chains to execute arbitrary code on the server. Gadget chains are sequences of existing classes (often from popular libraries like Apache Commons Collections, Spring Framework, or Groovy) that can be chained together during deserialization to trigger method invocations leading to command execution. Tools like ysoserial automate the creation of these payloads, making exploitation accessible even to less sophisticated attackers.

Data Tampering

Attackers modify serialized objects to alter application state or business logic. For example, an attacker might deserialize a shopping cart object, change the price field from $100 to $1, then reserialize and send it back. Similarly, serialized session objects containing user roles can be modified to escalate privileges from a regular user to an administrator. This type of attack is common when applications store serialized objects in cookies, hidden form fields, or local storage without integrity protection.

Denial of Service (DoS)

Malicious serialized objects can be designed to consume excessive server resources during deserialization. Attackers can create deeply nested object graphs or recursive structures that cause the deserialization process to consume all available memory or CPU. For example, a "HashSet collision" attack creates a HashSet with many objects that hash to the same value, causing O(n²) time complexity during deserialization. These attacks can crash application servers or make them unresponsive.

Impact

Insecure Deserialization vulnerabilities can have catastrophic consequences, often leading to complete system compromise. The impact severity is typically rated as critical due to the potential for remote code execution.

Remote code execution and system takeover

Attackers can execute arbitrary code on the server, install backdoors, create new user accounts, or pivot to other systems on the internal network. Once code execution is achieved, the attacker has the same privileges as the application process, potentially leading to complete server compromise.

Privilege escalation

By manipulating serialized session objects or user role data, attackers can elevate their privileges from a low-privilege user to an administrator. This allows them to access administrative functions, modify system configurations, and gain access to sensitive data that should be restricted.

Data breach and intellectual property theft

Once an attacker gains code execution or elevated privileges, they can access and exfiltrate sensitive data including customer information, financial records, trade secrets, and proprietary code. The breach may go undetected for extended periods, allowing attackers to establish persistent access.

Application and service disruption

Denial of service attacks through malicious deserialization can crash application servers, consume all available resources, or trigger cascading failures across distributed systems. This can result in significant downtime, revenue loss, and damage to brand reputation.

Prevention Checklist

Avoid native serialization formats entirely

The most effective prevention is to avoid deserializing untrusted data altogether. Use data-only formats like JSON, XML, or Protocol Buffers that do not support arbitrary code execution during deserialization. These formats only represent data structures and cannot instantiate arbitrary classes or invoke methods.

Implement strict allowlist-based deserialization filters

If you must use native serialization, implement filters that explicitly allowlist the classes that can be deserialized. In Java, use ObjectInputFilter; in .NET, use SerializationBinder. Reject any class that is not on the allowlist. Never use denylists—attackers can always find new gadget chains to bypass them.

Use cryptographic signatures for serialized data

If serialized data must be transmitted to clients and back, protect its integrity using digital signatures or HMAC. Sign the serialized data before sending it and verify the signature before deserializing. This prevents attackers from tampering with the data. Use authenticated encryption (e.g., AES-GCM) for additional confidentiality.

Run deserialization in low-privilege contexts

Isolate deserialization operations in sandboxed environments or containers with minimal privileges. If an exploit succeeds, the attacker's access should be limited to the sandboxed environment, preventing lateral movement or system-wide compromise. Use principle of least privilege for all application processes.

Monitor and log deserialization events

Implement comprehensive logging for all deserialization operations, especially those involving external data. Monitor for anomalies such as unexpected class types, large object graphs, or deserialization failures. Set up alerts for suspicious patterns that may indicate exploitation attempts.

Keep dependencies updated and audit for gadget chains

Regularly update all third-party libraries and frameworks, as many deserialization vulnerabilities arise from gadget chains in popular libraries. Use tools like OWASP Dependency-Check or Snyk to identify vulnerable dependencies. Consider removing or replacing libraries that are known sources of gadget chains (e.g., Apache Commons Collections 3.x).

Real-World Examples

2017

Apache Struts 2 (Equifax Breach)

The Equifax data breach, one of the most significant in history, was enabled by an insecure deserialization vulnerability in Apache Struts 2 (CVE-2017-5638). Attackers exploited this vulnerability to gain remote code execution, ultimately compromising personal data of 147 million people. The breach cost Equifax over $1.4 billion in settlements and remediation costs.

2016

Jenkins CI Server

Multiple insecure deserialization vulnerabilities were discovered in Jenkins, a widely-used continuous integration server. The vulnerabilities allowed unauthenticated attackers to execute arbitrary code on Jenkins servers by sending malicious serialized Java objects to the CLI interface or through the remoting protocol. Organizations running exposed Jenkins instances were at risk of complete server compromise.

2019

Oracle WebLogic Server

Oracle WebLogic Server suffered from critical deserialization vulnerabilities (CVE-2019-2725, CVE-2019-2729) that allowed remote, unauthenticated attackers to execute arbitrary code. Attackers actively exploited these vulnerabilities in the wild to deploy cryptocurrency miners and ransomware on vulnerable servers. The vulnerabilities affected thousands of internet-facing WebLogic installations.

2015

Apache Commons Collections

Security researcher Chris Frohoff discovered that Apache Commons Collections library could be used to create gadget chains for remote code execution through Java deserialization. This finding led to the discovery that countless Java applications using this popular library were vulnerable to RCE attacks. The research fundamentally changed how the security community viewed deserialization and led to widespread code remediation efforts.

Ready to Test Your Knowledge?

Put what you have learned into practice. Try identifying and fixing Insecure Deserialization vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.