Insecure deserialization in web applications

Every day, millions of application and API requests are serialized and deserialized to store, send, and receive all sorts of data. Insecure deserialization vulnerabilities allow attackers to trick an application into loading malicious code and are often exploited in high-impact attacks that result in remote code execution.

Insecure deserialization in web applications

Insecure deserialization is a vulnerability that is part of many attack chains against web applications and APIs. A vulnerable application will load data without validating it, allowing an attacker to manipulate the deserialization process and execute malicious code. While not always reported as a standalone vulnerability, insecure deserialization can have serious consequences for cybersecurity, including remote code execution (RCE), denial of service (DoS), and authentication bypass.

What is serialization and deserialization?

Before we get to insecure deserialization, let’s look at the whole concept of serialization and deserialization. Every application needs to store and transmit data, often including internal application objects. Serialization is the process of converting an object into a byte stream or another format (such as JSON, XML, YAML, or language-specific formats) so it can be stored or transmitted. For example, a game might save your current progress simply by serializing its internal state and storing it in a single file or database object to be loaded later.

Deserialization is the process of taking that serialized item (in whatever format) and converting it back into an internal application object. For our game example, that would mean reading the saved game data and recreating the game state from it. Deserialization is a routine and necessary part of transferring data structures between different systems and environments, especially when dealing with APIs, but implicitly trusting the data you’re deserializing can pose a serious security risk.

Did you know…?

 

The name of Invicti’s AppSec Serialized podcast is a pun on serialization! Check out the podcast on our site and on all popular podcasting platforms!

Why insecure deserialization vulnerabilities happen

Insecure deserialization happens when applications deserialize data without adequate validation, usually because only well-formed data from known systems is expected. In these cases, attackers can craft serialized data that, when deserialized, executes malicious code or has other harmful effects. The risk was serious enough for insecure deserialization to be listed as #8 in the OWASP Top 10 for 2017, though the restructured list for 2021 merged it into the more general category of Software and Data Integrity Failures.

Because it is a trust and validation issue rather than any specific security flaw, insecure deserialization is possible with many programming languages and serialization formats. Let’s look at some common examples.

Java deserialization: A common attack vector

Java deserialization vulnerabilities are among the most notorious due to the popularity of Java in high-value enterprise web applications. Java is also a language where everything has to be an object, making serialization a vital and built-in technique for data exchange. Any Java object that implements the Serializable interface can be converted into a byte stream simply using the writeObject method, while deserialization is then a matter of calling readObject to reconstruct a Java serialization object from the byte stream.

Whenever applications deserialize objects without validating their contents, attackers may be able to include a malicious payload in the serialized data. One attack vector for Java deserialization involves the use of so-called gadget chains—multiple deserialization function calls (typically ObjectInputStream) combined into a chain of operations that eventually leads to arbitrary code execution.

Deserialization vulnerabilities in other programming languages

Insecure deserialization is not limited to Java. In PHP, the unserialize function is a frequent target when handling serialized data from untrusted sources. Python provides the pickle module as a flexible and convenient means of serializing even complex data types, but the Python documentation itself warns about the dangers of insecure deserialization:

The pickle module is not secure. Only unpickle data you trust.

 

It is possible to construct malicious pickle data which will execute arbitrary code during unpickling. Never unpickle data that could have come from an untrusted source, or that could have been tampered with.

In fact, this warning applies not only to this Python module but also to other serialization formats like JSON, XML, and YAML. Deserialization can be especially risky when working with APIs, which rely heavily on serializing application data and providing it via an endpoint. Without careful validation and possibly also anti-tampering measures such as hashes or signatures, applications that consume deserialized API data may be at risk—but what’s the worst that can happen?

Attacks made possible by insecure deserialization

Insecure deserialization can be a part of many attack chains depending on how a specific application handles deserialization and what other vulnerabilities it includes. In general, any injection attack might be possible, starting with SQL injection or cross-site scripting (XSS) to reveal sensitive information or tamper with stored data, but more serious consequences of insecure deserialization include: 

  • Remote code execution: By injecting malicious serialized data, attackers can execute arbitrary code on the server, gaining complete control over the system. This is the most dangerous consequence of insecure deserialization, as illustrated by its use as the final step in the MOVEit Transfer attacks.
  • Denial of service: Attackers can craft serialized data that causes the application to crash or become unresponsive, resulting in a DoS attack. For example, injecting malicious XML data into an XML deserialization process can lead to memory exhaustion or infinite loops (as with XXE), causing the application to fail.
  • Authentication bypass: In some cases, deserialized objects might contain information that affects the authentication/authorization process (tokens or session data). Attackers can tamper with the serialized data to bypass auth checks and access protected areas of the application.
  • Object injection: In object injection attacks, attackers modify serialized objects to change application behavior after deserialization, such as altering user permissions or modifying data structures.

Mitigating insecure deserialization vulnerabilities

Preventing insecure deserialization attacks requires careful input validation and data verification. Here are some best practices for mitigation:

  • Validate serialized data: Always validate the integrity of serialized data before deserializing it. Use digital signatures or cryptographic hashes to ensure the data has not been tampered with during transmission or storage.
  • Avoid deserializing untrusted data: Don’t deserialize data from untrusted sources if you can help it, especially user inputs or data from external APIs. If deserialization is necessary, treat any such data as untrusted and potentially malicious, and enforce strict validation.
  • Use safer serialization libraries: Where possible, use safer serialization libraries to have more control over the deserialization process compared to using a binary format with dynamic object definitions. Again, ensure that all data is sanitized and validated before deserialization.
  • Restrict object deserialization: If you’re working with Java deserialization, be very careful with built-in tools like ObjectInputStream. Consider implementing a more restricted method that only allows specific Java objects to be deserialized.
  • Implement access control: Restrict access to the deserialization process and ensure that the application runs with minimal privileges during deserialization to reduce the risk of privilege escalation or RCE.
  • Monitor and patch dependencies: As with all other vulnerabilities, regularly review and update your application’s dependencies to minimize the risk of third-party libraries introducing insecure deserialization vulnerabilities.

Because insecure deserialization is usually part of a more elaborate attack chain and source code analysis alone is not enough to detect if your app is vulnerable, it’s also crucial to regularly test your applications using advanced dynamic testing tools and periodic penetration testing.

Read how Invicti DAST can help you automate a large part of your pentesting and how bringing security testing in-house allowed one Invicti customer to cut their penetration testing budget by more than half.

Zbigniew Banach

About the Author

Zbigniew Banach - Technical Content Lead & Managing Editor

Cybersecurity writer and blog managing editor at Invicti Security. Drawing on years of experience with security, software development, content creation, journalism, and technical translation, he does his best to bring web application security and cybersecurity in general to a wider audience.