Exploiting PHP deserialization

Intro to PHP object injection vulnerabilities

Vickie Li
Vickie Li
May 17, 2020 · 5 min read
Image for post
Image for post
PHP: Hypertext Preprocessor | Logo by Colin Viebrock on php.net

Serialization is when an object in a programming language (say, a Java or PHP object) is converted into a format that can be stored or transferred. Whereas deserialization refers to the opposite: it’s when the serialized object is read from a file or the network and converted back into an object.

Insecure deserialization vulnerabilities happen when applications deserialize objects without proper sanitization. An attacker can then manipulate serialized objects to change the program’s flow.

Today, let’s talk about PHP object injections. They are insecure deserialization vulnerabilities that happen when developers deserialize PHP objects recklessly.

Serialization in PHP

To understand PHP object injections, you have to first understand how PHP serialize and deserialize objects.

When you need to store a PHP object or transfer it over the network, you use serialize() to pack it up.

serialize(): PHP object -> plain old string that represents the obj

When you need to use that data, use unserialize() to unpack and get the underlying object.

unserialize(): string containing object data -> original object

For example, this code snippet will serialize the object “user”.

<?phpclass User{
public $username;
public $status;
}
$user = new User;
$user->username = 'vickie';
$user->status = 'not admin';
echo serialize($user);?>

Run the code snippet, and you will get the serialized string that represents the “user” object.

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"not admin";}

Let’s break this serialized string down! The basic structure of a PHP serialized string is “data type: data”. For example, “b” represents a boolean.

b:THE_BOOLEAN;

“i” represents an integer.

i:THE_INTEGER;

“d” represents a float.

d:THE_FLOAT;

“s” represents a string.

s:LENTH_OF_STRING:"ACTUAL_STRING";

“a” represents an array.

a:NUMBER_OF_ELEMENTS:{ELEMENTS}

And finally, “O” represents an object.

O:LENTH_OF_NAME:"CLASS_NAME":NUMBER_OF_PROPERTIES:{PROPERTIES}

So we can see our serialized string here represents an object of the class “User”. It has two properties. The first property has the name “username” and the value “vickie”. The second property has the name “status” and the value “not admin”.

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"not admin";}

When you are ready to operate on the object again, you can deserialize the string with unserialize().

<?phpclass User{
public $username;
public $status;
}
$user = new User;
$user->username = 'vickie';
$user->status = 'not admin';
$serialized_string = serialize($user);
$unserialized_data = unserialize($serialized_string);
var_dump($unserialized_data);
var_dump($unserialized_data["status"]);
?>

Unserialize() under the hood

So how does unserialize() work under the hood? And why does it lead to vulnerabilities?

What are PHP magic methods?

PHP magic methods are function names in PHP that have “magical” properties. Learn more about them here.

The magic methods that are relevant for us now are __wakeup() and __destruct(). If the class of the serialized object implements any method named __wakeup() and __destruct(), these methods will be executed automatically when unserialize() is called on an object.

Step 1: Object instantiation

Instantiation is when the program creates an instance of a class in memory. That is what unserialize() does. It takes the serialized string, which specifies the class and the properties of that object. With that data, unserialize() creates a copy of the originally serialized object.

It will then search for a function named __wakeup(), and execute code in that function. __wakeup() reconstructs any resources that the object may have. It is used to reestablish any database connections that have been lost during serialization and perform other reinitialization tasks.

Step 2: Program uses the object

The program operates on the object and uses it to perform other actions.

Step 3: Object destruction

Finally, when no reference to the deserialized object instance exists, __destruct() is called to clean up the object.

Exploiting PHP deserialization

When you control a serialized object that is passed into unserialize(), you control the properties of the created object. You might also be able to hijack the flow of the application by controlling the values passed into automatically executed methods like __wakeup() or __destruct().

This is called a PHP object injection. PHP object injection can lead to variable manipulation, code execution, SQL injection, path traversal, or DoS.

One possible way of exploiting a PHP object injection vulnerability is variable manipulation. For example, you can mess with the values encoded in the serialized string.

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"not admin";}

In this serialize string, you can try to change the value of “status” to “admin”, and see if the application grants you admin privileges.

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:5:"admin";}

It’s even possible to achieve RCE using PHP object injection! For example, consider this vulnerable code snippet: (taken from https://www.owasp.org/index.php/PHP_Object_Injection)

class Example2
{
private $hook;
function __construct(){
// some PHP code...
}
function __wakeup(){
if (isset($this->hook)) eval($this->hook);
}
}
// some PHP code...$user_data = unserialize($_COOKIE['data']);// some PHP code...

You can achieve RCE using this deserialization flaw because a user-provided object is passed into unserialize. And the class Example2 has a magic function that runs eval() on user-provided input.

To exploit this RCE, you simply have to set your data cookie to a serialized Example2 object with the hook property set to whatever PHP code you want. You can generate the serialized object using the following code snippet:

class Example2
{
private $hook = "phpinfo();";
}
print urlencode(serialize(new Example2));
// We need to use URL encoding since we are injecting the object via a URL.

Passing the above-generated string into the data cookie will cause the code “phpinfo();” to be executed. Once you pass the serialized object into the program, the following is what will happen in detail:

  1. You pass a serialized Example2 object into the program as the data cookie.
  2. The program calls unserialize() on the data cookie.
  3. Because the data cookie is a serialized Example2 object, unserialize() instantiates a new Example2 object.
  4. unserialize() sees that the Example2 class has __wakeup() implemented, so __wakeup() is called.
  5. __wakeup() looks for the $hook property of the object, and if it is not NULL, it runs eval($hook).
  6. $hook is not NULL, and is set to “phpinfo();”, so eval(“phpinfo();”) is run.
  7. RCE is achieved.

Conclusion

If you can control the serialized string that is passed into unserialize(), you can control the entire object that is generated. If the object is not sanitized before deserialization, you might be looking at a PHP object injection vulnerability. You can use PHP object injections to achieve variable manipulation, SQL injection, path traversal, DoS, or even RCE!

The Startup

Get smarter at building your thing. Join The Startup’s +776K followers.

Vickie Li

Written by

Vickie Li

Professional investigator of nerdy stuff. Hacks and secures. Creates god awful infographics. https://twitter.com/vickieli7

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +776K followers.

Vickie Li

Written by

Vickie Li

Professional investigator of nerdy stuff. Hacks and secures. Creates god awful infographics. https://twitter.com/vickieli7

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +776K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store