A8: Insecure Deserialization 2017 OWASP
Insecure serialization has historically been seen as a super hard to grasp vulnerability, almost like a black box but while it does contain it’s challenges, so does every other issue type on the OWASP top 10. serialization is a technique used to convert an object into a byte stream for it to be stored somewhere or passed on to another system. As these serialized objects undergo deserialization, dangerous issues might arise. This issue is often referred to as “Marshalling” and “Unmarshalling” in PHP and as “pickling” and “unpickling” in python. Let’s have a look at this issue type from the Top 10 OWASP.
What is serialization?
First we will need to get into Serialization, it is a process that occurs in applications when data needs to be transmitted and stored. This can be a very useful technique because two applications might have a completely different internal structure.
What Is Insecure Deserialization and how does it work?
While objects might be serialized to store or transfer them, at some point they might need to be deserialized again. In this deserialization logic, vulnerabilities might reside. The biggest problem with this algorithm is that the deserialization process does not discriminate as it will deserialize any object that the application might have access to. The attacker might modify attributes of the object or even insert a new one entirely. The serialization process might leave the application vulnerable in a number of ways depending on the applications logic and the object that is serialized. We will look at a few examples but please know there are more ways to exploit this vulnerability.
One example, let’s say our object contains the attribute “isSuperUser:false”. An attacker might simply deserialize the object to change the attribute before serializing it again. The server will then take this new object and treat the user as a SuperUser. This is only one of the examples of how insecure deserialization can occur however.
we need to be aware that attackers might just as well add a parameter they suspect the server uses as a property for example, if the server does use isSuperUser as an attribute of the serialised object, it might still accept it when deserializing it.
Via the logic of the application
For this example I want to bring up functionality that is meant to update a user's profile settings. As part of this functionality there exists a call to update a user's social media links. Normally the user can only specify the username part of the URI (for example in https://twitter.com/theXSSrat the user might normally only be able to edit the /theXSSrat part) but as part serialised object, the user can find the full URL.
After editing the URL to the users own attack servers they notice a request coming in which means a blind SSRF has been triggered by abusing business logic. This full URL should never be in the object in the first place and the attackers were able to abuse it.
Via magic events
We first of all need to start by explaining what a magic event is because that might not be clear to everyone. A magic event is a method in a programming language that gets called automatically. For example in PHP we have the following magic method: __sleep(). The problem with these magic methods is that they might be vulnerable to attacks if they handle data. This can happen through deserialized objects for example.
Via the insertion of objects
Besides the examples we have seen before, which rely heavily on insertion or adaptation of data, we can also insert arbitrary objects sometimes. This is possible because in object oriented programming, the methods that an object can access are delegated by it’s parent class. This means for example that normally, a user object has no access to the yet unreleased functionality to make a user admin. This resides on the URI /makeAdmin.php. The attacker can send a serialized object belonging to the class makeAdmin to a non expecting endpoint to change their profile picture which also expects a serialized object. The problem is that for deserialization the code does not care about what object is passed to it. It will happily deserialize it and while it may cause an error due to unexpected parameters, the damage is done and the user made themselves admin.
Types of Insecure Deserialization
We have already looked at a few examples but these can all be brought under one of three types of Insecure Deserialization.
- Asynchronous deserialization attacks
This is also known as stored insecure deserialization because it requires the attacker to enter an attack vector which will get stored and later on deserialized. This is often verbose, as it gives back error information if it fails and the desired result if it does not but it can also happen that the attack vector is triggered later where the attacker themselves have no access to. This would be classified as blind deserialization attacks
- Blind deserialization attacks
Blind deserialization attacks occur when attackers send an attack vector to an application which does get stored but the attacker is not able to trigger the attack vector due it being triggered from a back-end system for example. These types of vulnerabilities are hard to detect and require the attacker to have in-depth knowledge of the application as they need to know what object to test.
- Deferred-execution deserialization
It’s called deferred because the attack vector will be executed by a secondary system that’s made to clean up unused objects such as garbage collection in java or file.close() in PHP.
We’ve been talking a lot about PHP up to this point but this is not the only programming languag that insecure deserilisation can be found in. There are many different programming languages that all have their own unique twist on the process of serilisation. As we can see on this page on wikipedia there are many different serilisations formats and we will be taking a practical look at some of them. With so many serialisation formates, it is no surprise that a big portion of the vulnerabilities is yet to be discovered.
Python pickle and marshal
The first example we will look at is how python handles it’s serialisation and we can conclude that there are two ways python handles this. One is called pickling and the other marshling which is also the term ruby uses for deserilisation.
First of all, we need to import pickle if we want to use it of course.
This will allow us to import pickle from python. After which we can start using the module.
We can then append data to a variable and pass it to the pickle function together with a file to serialise an on object.
The reverse operation is just as easy
data = pickle.load(file)
We can also see the ffunctionality in ruby by importing the marshalling module and repeating the same steps.
data = Marshal.dump(“a”)
As you can see, we do not have to write this data to a file to serialise the object. We can just as easily deseriliase it.
deseriliased_data = Marshal.load(data)
private void writeObject(java.io.ObjectOutputStream out)
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
Java has 3 methods available to use, to do this we need to implement it first.
class s implements java.io.Serializable
Next we need to call the writeobject method on an objectOutputStream.
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
Finally we can serialize the object
// Method for serialization of object
We can do almost exactly the same but in reverse. We just need to be careful we are using another method readObject whereas the serialisation will user writeObject.
// Reading the object from a file
FileInputStream file = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(file);
// Method for deserialization of object
object1 = (Demo)in.readObject();
Up unto this point we only talked about programming lanuages but these issue can also arise in data formats themselves such as JSON as it is also serialised data. A great example can be found here:
This attacker was able to abuse insecure deserilsation to execute code on the server.
Examples of unsafe deserialization
The first example we will go over is a CVE that has been found in an application called “Concrete5”. This application had a logfile that was checked on existing. However this log file appears to have a serialised object which could be injected with objects allowing for arbitrary PHP code execution.
More information can be found over at https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-36766
A second example we can find is pretty straight forward. The user object has been serialised and contains the account type. Initially this is set to “user” but it can be deserialized and changed to admin before serialising and sending it to the server again.
When changing the parameter in bold to admin and serialising it again, the attacker can pass it on the server and elevate their privileges.
How to detect Insecure Deserialization?
Detection of this vulnerability will depend on what programming language is being used. Every language has their own way of indicating serialised objects so we have to learn how to recognise them per language. We have to ensure that we check all the data that is being used by the application and when we identify an object, we can try to deserialize it ourselves and see if we can exploit it.
How can I prevent Insecure Deserialization
The first tip is the simplest of all but it’s not easy to implement, all serialisation should be avoided and we should opt for a much simpler data format but this is not always possible. When organisations use a lot of serialisation already, it’s hard to faze it out but it’s recommended to also go over to simpler data formats. These are formats like JSON and XML.
If we do have to use serialisation however, we have to verify all the user controlled data and we need to make sure it contains sane data that can not be abused. For example, a user should never be able to change their own account type to admin. We also need to be aware that there have been proof of concepts on how to bypass this kind of use of strict constraints on type and parameters.
One strategy we can also take is to keep our serialization code into a sandbox or low privilege environment to lower any eventual impact that might follow a successful hack.
Like the “insufficient monitoring and logging” chapter of the OWASP top 10, we need to comply with that and make sure that we log any exceptions that occur while deserialization and investigate it later.
It also helps to ensure the API security or other security mechanisms check all incoming and outgoing data for the correct parameters and it being the correct object.
Tools to protect against Insecure Deserialization.
There are several top tools availale to use which help us in our battle against this issue type. Some will be more lanuage specific such as Java Deserialization Scanner which is a burp suite extension. Burp suite pro tends to indicate serialised objects as well which might be sufficient for some.
I have personally used ysoserial as well which is a great tool to help generate Proof of concepts for insecure deserialisation.
FindBugs plugin is an application that will also help us find vulnerabilities.
Furthermore there is Jinfinity which is a toold designed to fill up the memory of your target, this causing a DoS attack.
Serianalyzer is a java specific tool which will perform static code analysis.
Lastely we have the option to use Serializekiller script which will scan for the default set of ports but you can configure it to work for all ports.
This issue type has many complexities but they are not something that can’t be foreseen or tested for. We need to train testers to recognise serialised objects and to exploit them. It would help the cybersecurity community immensely.