PHP Object Injection

Object injection is a type of attack that allows arbitrary data to be written to variables in PHP classes.  This is a versatile attack that can potentially allow a broad range of compromises to the confidentiality or integrity of data as well as unauthorized access to server resources.  Object injection exploits functionality inherent to PHP classes as well as a supported method of storing variable data called serialization.  This article will provide a walkthrough of an object injection attack but first, a brief overview of its enabling components.

As an object oriented language, PHP supports the use of classes that group together functions and variables to be called and reused by different scripts.  Functions that are used within a class are referred to as methods while variables within a class are called properties.  An object is a running instance of a class that contains the data stored by the class properties. PHP also supports the function serialize() that allows for the storage of variables and their data in the form of specially formatted strings.  Likewise, the function unserialize() loads serialized data into variables.  The unserialize() function is typically the entry point for an object injection attack as PHP allows for the serialization of an entire object along with its properties, more on this later.

I will now demonstrate an object injection attack against level 26 of the Natas hacking game.  I have chosen this level due to its use of a class that creates files on the server and because exploiting it requires manipulation of private class properties.  Private properties can only be accessed by their defining class, thus further distinguishing the attack.  These conditions offer a broad perspective over the range of direct and indirect vulnerabilities offered by object injection.

At first glance we see that this level allows the user to draw a line on a blank image by submitting graph coordinates in the given form fields:


However, looking through the source code, we see that it begins by declaring a class called Logger() that creates and modifies log files on the server.  Most noteworthy is that the class properties that store the values for the log file path and data are written to by the __construct() method:


This is the first condition that allows for object injection, __construct() is one of several “magic methods” that are called based on the execution flow of the class.  A magic method must be present to implement an object injection attack and in this case the __construct() method is called upon class instantiation, the creation of an object.  The second thing to note about this class is that the location and contents of the log files it creates are based on the value of the private properties $logFile, $initMsg and $exitMsg.  Manipulating their values will be central to our attack.

The code that follows this class is mainly concerned with displaying the image and drawing lines, however what is most notable is the storeData() function.  This function is written so that the line coordinates submitted by the user are stored in the “drawing” field of their session cookie.  Crucially, this is done by serializing the coordinates and converting the string to base64:


The same process is reversed in the function drawFromUserData() when it comes to reading existing session data from the cookie.  The base64 data is decoded and unserialize() is called to load the coordinates into the $drawing array:


We have now pinpointed the attack surface that will allow us to execute our attack. The hypothesis that shall be pursued is that arbitrary data can be passed to the unserialize() function via the cookie value “drawing”.  If this string is a serialized Logger object it can be crafted to modify property values such that arbitrary files can be created on the server. This will in turn allow us to create and execute a PHP script that retrieves the contents of the password file.

The success of this attack depends on crafting the injected string with the correct syntax. To this end I have written a PHP script that copies the Logger() class and outputs a serialized instance of it:


This outputs:

O:6:”Logger”:3:{s:15:”LoggerlogFile”;s:22:”/tmp/natas26_param.log”;s:15:”LoggerinitMsg”;s:22:”#–session started–# “;s:15:”LoggerexitMsg”;s:18:”#–session end–# “;}

A brief explanation: the string begins with O:6 which indicates that it is a stored object with 6 characters in its given name.  The object name is then followed by the number of properties the serialized object contains, in this case it is three. The property names and values are encapsulated within curly brackets, each preceded by an “s” to indicate that it is a string and the number of characters it contains.

Note that each of the property names logFile, initMsg and exitMsg are preceded by the name of the object, Logger, this indicates that they are private properties.  However, what you might have also noticed is that the number of characters given is 15, whereas LoggerlogFile for example actually contains only 13 characters.  This is because upon serialization of a private property, the class name is also encapsulated by null bytes: \0 that are usually hidden. This a feature of the PHP language known as property name mangling, that aims to distinguish different types of serialized properties if they share the same name.

It is therefore critical to include these null bytes in our serialized object to modify these private properties and create our script on the server.  I have found that the best way to do this while protecting the integrity of the object is to use the string below and pass it to the PHP function base64_encode().  Double quotes are escaped in order for PHP to process it as a single string:

O:6:\”Logger\”:3:{s:15:\”\0Logger\0logFile\”;s:39:\”/var/www/natas/natas26/img/pwdecho2.php\”;s:15:\”\0Logger\0initMsg\”;s:22:\”#–session started— \”;s:15:\”\0Logger\0exitMsg\”;s:62:\”<?php echo file_get_contents(\”/etc/natas_webpass/natas27\”); ?>\”;}

This object changes the values of the class properties such that the “log file” created is a PHP script that can be executed in a web facing directory. The contents of the file is a single line of code that retrieves and outputs the contents of the password file: <?php echo file_get_contents(“/etc/natas_webpass/natas27”); ?>. The exit message is left unchanged.

The serialized string converted to base64 is injected using Burp Suite to be correctly processed by the code:


Accessing our script in the img directory reveals the password:


Final Thoughts

This attack was enabled primarily by the use of the unserialize() function to handle user input. As demonstrated above, this function allows arbitrary class instantiation so long as it is used to processes incoming data. The first remediation measure to take in protection of such an attack is to use the functions json_encode() and json_decode() in place of serialize() and unserialize(). The function json_encode() will store only variable data, not an object. Therefore, json_decode() cannot be used in the above described attack as there is no way for an attacker to input an arbitrary object name.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s