Insecure Deserialization: The Hidden Threat Behind Blindly Trusting User Inputs
In this article, we’ll cover what Insecure deserialization is and evaluate the consequences of blindly trusting any user-supplied input into the application and understand how a potential attacker can utilize this weakness in launching a further attack. Let’s dive into the ins and outs of insecure deserialization vulnerabilities.
What is (de)serialization?
The fundamental objective behind conducting serialization and deserialization of objects is straightforward. Computers or network infrastructures don’t understand language or numbers the way humans do. Everything on a computer is represented as streams of binary numbers or bytes that the computer can easily understand, process or store.
Serialization is the process of converting an object into a stream of bytes to store the object or transmit it to memory, a database, or transfer over a network. It also allows the object to be recreated when needed.
Deserialization is the opposite. It is the process of reconstructing the object from the serialized state. It is the reverse operation of serialization where the stream of bytes is converted into a form of an object.
What causes insecure deserialization?
The root cause of an insecure deserialization vulnerability mainly arises in the fact that developers consider that the deserialized objects are often to be trustworthy and trust the data provided by a sanitized object more than classical user input. By assuming that users cannot read or manipulate the serialized data effectively, it is directly passed into the functions without verification.
When a software deserializes user-controlled data without any verification or validation, this potentially enables an attacker to manipulate serialized objects to pass malicious content into the application code or the vulnerable server.
If the deserialization process is not adequately secured, attackers can exploit it to inject a malicious serialized object into an application and can perform different kinds of malicious attacks, such as Code Injection, SQL Injection, Path Traversal, and Application Denial of Service, depending on the context. Attackers can use insecure deserialization as an entry point to a system, from which they can pivot to further attacks.
User-provided data such as URL parameters, POST data payloads, or cookies should always be considered untrusted and tainted.
To understand the vulnerability further, let’s take a look at how serialization and deserialization work in PHP and Python.
Insecure Deserialization in PHP
Below is an example of serialization in PHP.
The serialize() function generates a storable representation of a value so that it can be stored in a file, a memory buffer, or transmitted across a network.
PHP will serialize data in a mostly human-readable format which can be seen below.
The following serialized data can be interpreted as follows:
As a simple example, if an attacker spotted this serialized object in an HTTP request as a form of a session cookie as shown below:
They might decode it to find the following byte stream:
Let’s say the website relies on this session cookie to check the user role of the logged-in user. A malicious attacker could easily manipulate the value of the “role” parameter to “admin” or any higher privilege user and at no point is the authenticity of the serialized object checked. This data is then passed into the conditional statement and, as the application depends upon the serialized object to determine the user role, the attacker could efficiently escalate the privilege which would allow for an easy privilege escalation.
Insecure Deserialization in Python
The Python pickle module allows you to serialize and deserialize data. Essentially, this means that you can convert a Python object into a stream of bytes and then reconstruct it later in a different process or environment by loading that stream of bytes.
Since there are no effective ways to verify the pickle stream being unpickled, it is possible to provide malicious shell code as input, causing remote code execution and other critical vulnerabilities.
While referring to the Python docs for pickle one cannot miss the following warning:
➔ Warning: The pickle module is not secure. Only unpickle data you trust.
The pickle interface provides four methods: dumps, dump, loads, and load.
- The dumps() method serializes to a string.
- The dump() method serializes to an open file (file-like object).
- The loads() method deserializes from a string.
- The load() method deserializes from an open file-like object.
The Pickle module serializes the Python object in a binary format, which is not human-readable. The pickled representation we’re getting back from dumps will somewhat look like this:
Let’s walk through this vulnerable Python snippet below and understand how the pickle module works:
The cookie value is encoded and stored within your browser. Further, the value of this cookie is decoded and then deserialized. In the snippet above, we can see how the cookie is retrieved and then deserialized via pickle.loads
What’s wrong here is that the cookie value is encoded in base64 format and when the cookie is retrieved back to its original form without any validation, it lets an attacker manipulate the encoded value, and that’s where the vulnerability arises.
As the cookie value is being deserialized in the form of base64 format, we must encode our own commands in base64 format and replace them with the existing value so that the malicious code will be executed.
To exploit this vulnerability, we will be using the below code snippet to construct a malicious cookie value with command injection Poc and launch a reverse shell on the target host once the cookie value has been deserialized.
Run the above code snippet to generate a malicious cookie value with a serialized data stream using pickle.
In the vulnerable running application, the application parses the cookie value as a Base64, hence copy and paste the extracted malicious cookie value generated above into the “encodedPayload” cookie parameter in the browser as shown below, and ensure that the Netcat listener is still running on your machine.
Due to an improper implementation and a lack of validation, the user supplied cookie value would be decoded and deserialized by the application server. During the deserialization process, the malicious object serialized in the payload is unpacked and command execution takes place and a remote shell is obtained.
Deserialization when implemented insecurely can have a heavy toll on the underlying systems so it’s advised that one must follow all the precautionary guidelines and implement proper prevention techniques when implementing these methods.
Conclusion
The best way to protect against deserialization vulnerabilities is to avoid deserializing user input entirely. If you must deserialize user input, take measures to ensure that the data has not been tampered with, and do not use generic deserialization features.
Stay safe out there, and if you’ve enjoyed this read, feel free to reach out to me on LinkedIn. Your thoughts and questions are more than welcome!