PHP Type Juggling Simplified

Greetings to all. In this blog post, our Principal Consultant Rohit Misuriya has talked about the PHP Type Juggling vulnerability. The per-requisites, attack vectors, exploitation scenarios, recommendations, practice labs, and more information about the issue has been provided by him in this blog.

Since its introduction, PHP has maintained a prominent level of popularity, and statistics suggest that many web application developers favour it. This is one of the reasons why the information security community has been intrigued about PHP for so long. The use of PHP in numerous Content Management Systems (CMS), the source codes for which are freely available on websites like GitHub, is another factor contributing to PHP’s fame in the realm of information security. Due to the open-source nature of these source codes, various vulnerabilities that are unique to programmers and systems that use PHP as their primary programming language, have been identified.

There have been a lot of vulnerabilities in PHP-based apps, both in terms of quantity and variety. However, most security experts and developers overlook or disregard them as  not all disclosed vulnerabilities are easy to comprehend and identify. In this blog post, we will talk about PHP Type Juggling (Loose Comparison), one such vulnerability that belongs to this category, so that the readers have a better knowledge of this mysterious vulnerability as to why it exists, and how an attacker may exploit it etc. Furthermore, we have built a practice lab on our platform Vulnmachines for you to explore and get direct experience of this vulnerability.

TL;DR

  1. PHP type juggling vulnerability occurs when a loose comparison operator (== or!=) is used in the place of a strict comparison operator (=== or!==) in a situation where the attacker has access to one of the variables being compared. 
  2. This vulnerability may cause the application to provide an unexpected true or false response and may result in serious authorization and/or authentication problems. 
  3. For the convenience of developers, loose comparisons provide a set of operand conversion rules.
  4. For direct practice, Vulnmachines provides an intentionally vulnerable testing playground.

PHP comparison methods

The term “type juggling” or “type casting” refers to a PHP functionality. This indicates that PHP will first transform variables of disparate kinds to a common, comparable type before comparing them.

For instance, in the following example, the program compares the string “4” and the integer 4:

The program will execute flawlessly and print “PHP can evaluate strings and integers.” This behavior is especially useful if we want our application to adapt to different user input types. However, it is important to note that this behavior is also a significant source of security flaws and bugs.

For example, PHP will try to extract the integer from the string when comparing the string “9 phones” to the integer 9. This comparison will therefore yield a True result. (PHP-5)

This kind of loose type comparison behavior is rather typical in PHP, and many built-in functions operate in a similar manner. Although it is clear this could be detrimental, how exactly can an attacker take advantage of this behavior?

What causes the vulnerability

This unique feature of PHP is frequently exploited to get around authentication restrictions.Suppose the PHP code for handling authentication is as follows (PHP-5):

Then, as this evaluates to True, entering an integer input of 0 would successfully log you in (PHP-5):

PHP Comparisons: Strict
PHP Comparisons: Loose
Magic Hashes

If the computed hash only contains numbers after the first “0e” (or “0..0e”), PHP will treat the hash as a float. A password hash that begins with “0e” will always appear to match the below strings, regardless of what they actually are. The consequence is that when these magic hash numbers are compared to other hashes and treated as the number “0,” the comparison will result in a True. Consider the scientific notation “0e…” as standing for “0 to the power of some value,” which is always “0.” The string is interpreted by PHP as an integer.

Example:

Scenarios of Exploitation

This vulnerability, though, is not always exploitable, frequently requires the use of a deserialization bug. This is due to the fact that POST, GET, and cookie values are typically supplied to programs as strings or arrays.

PHP would compare two strings if the program received the POST parameter from the previous example as a string, therefore no type conversion would be required. And it goes without saying that “0” and “password” are separate strings.

However, if the program accepts the input through functions like json_decode() or unserialize(), type juggling issues can be exploited. The end-user would be able to specify the kind of input that is passed in in this way.

Take a look at the JSON blobs above. The first would force PHP to understand the input as an integer, whereas the second would cause PHP to consider the id parameter as a string. As a result, the attacker will have a fine-grained control over the type of incoming data and can therefore take advantage of type juggling concerns.

Industry Standards and Recommendations

There are numerous actions you may take as a developer to stop these vulnerabilities from occurring.

Use Strict comparison

Always strive to use the type-safe comparison operator “===” rather than the loose comparison operator “==” when comparing values. The operation will only return True if the types of the two variables match, preventing PHP from engaging in type juggling. Therefore, the expression (4 === “4”) will return False.

Use “Strict” option for the comparison functions

Always read the PHP manual for specific functions to see if they employ type-safe or loose comparison. Check if using strict comparison is an option, then include that option in your code.

For instance, PHP’s in_array() by default employs loose comparison. But, if you specify the strict option, it will switch to type-safe comparison.

Always avoid using a function that uses loose comparison and look for alternatives.

But, if we specify the strict option, it will switch to type-safe comparison as shown below:

Do not use typecasting before comparison

It is best to avoid typecasting before comparing values because it will provide outcomes remarkably similar to type juggling. The type-safe operator, for instance, perceives the following three variables as different, that is before typecasting.

While “4 str” will change to the integer 4 after typecasting, PHP will only keep the number that was extracted from the string.

Practice Labs

As a team of advanced penetration testers and security researchers, we passionately believe in a hands-on approach to cyber security. As a result, we have published a PHP Type Juggling lab on our platform Vulnmachines. The readers can further understand this vulnerability and its exploitation by practicing it in our labs which resemble real-life situations.

Detection of the vulnerability

The option to download the source code is available in the lab, as seen in the screenshot below.

Forget Password

There is a function in the source code that creates a link for the user to reset their password which looks like this.

http://{domain}/Reset.php?user=admin&time=1671181751&sig=8d03edbc

Upon closer examination of the function in the source code, we discover that the application creates a random reset token and stores it in the database. It then generates a sha256 hash using the following 4 parameters:

  • User’s username
  • Random token from the database
  • Current time
  • User’s current password

 It then uses the first eight letters of the hash created above to create the password reset link.

Reset.php

As seen in the below mentioned screenshot, a function called “ValidateLink” checks the URL to reset the password before allowing the user to do so. To validate the link, the function obtains the random token and current password of the user from the database and uses the  username and time value from the URL to generate the hash which it then compares (first 8 characters) with the value supplied in the sig parameter of the URL.

Reset.php

However, as we can see the developer of that function used a loose comparison to compare the two hashes. So, let’s see if we can exploit it. 🙂

Exploitation

We can see that there are actually four parameters used to create the hash, but only the user and time parameters are under the attacker’s control. Since the user parameter is static and the function retrieves the password and token from the database based on the username, the only parameter an attacker can use to exploit the vulnerability is time.

Let’s assume that the username is “admin,” the random token is “56532800904”, the value of time is “1671181751”, and the password is “Admin@123” to better comprehend the procedure. The sha256 hash of these values in this instance will be:

8d03edbc280414b9c6a82658a866087fdd945f8d72b293c321763b8ce5dadb69

Hash Generation

The link to reset the password will appear as follows because the developer only used the first 8 letters of the hashed value to create it:

http://{domain}/Reset.php?user=admin&time=1671181751&sig=8d03edbc

As we’ve already seen, the developer is validating the link to reset your password by using a loose comparison. Therefore, if we are able to pass the time value in such a way that the “generateSig” function creates a magic hash that begins with 0e followed by numbers, then we can bypass the loose comparison. To do so, in the link of reset the password, we need pass the hash 0e000000 so that PHP will try to convert both strings to integers before comparing them, as shown in the screenshot below, leading to a type juggling issue:

Let’s create some hashes with 3 variables fixed (username, token and current password) but with different values of time.

We can see that in this case, if we utilize the time value of 1671194321, we can bypass the comparison as displayed below as the resulting hash starts with 0e followed by numbers (first 8 characters):

Now, since we are unaware of the user’s token and actual password, we must apply brute force to get the perfect value of time until we find the ideal link. As we have no control over server-side code, the next concern is how can we know if the link is perfect?

Since we already know that the response length will change when the link is perfect, we can utilize this situation in our attack to get the perfect link.

Exploit Writing

As shown below, we must first develop a code to determine the response length of a broken password reset link.

Length.py
Length of the Response

Now that we are aware that the non-working link’s response length is 1143, we can use it to find the right link. To brute force, we must design an exploit that uses a loop to send the various requests to the server with an increasing value of time until the response length changes.

Exploit.py
Password Reset Link
Flag

And it works! We have got the perfect password reset link. Readers can find the exploit here.

Note: We advise validating the correct response length before using the main exploit and updating the exploit as necessary because the length in the exploit may change depending on the response length the user receives. Also we need to keep in mind that there is a restriction on time parameter, the same value of time parameter will not be valid after 6 hours as mentioned in the “tokenExpiry” function.

Conclusion

PHP is a fantastic language that is adaptable, practical, and simple to learn. However, this adaptability has a price. If the developer is careless, several PHP features might result in vulnerabilities.

One aspect that has the potential to lead to vulnerabilities is Type Juggling. Be particularly cautious when comparing values, and always be aware of how your application is operating.

References

Demystifying PHP Object Injection

Hello readers, in this blog post, our consultant Aditya has discussed the PHP Object Injection vulnerability. He explains the vulnerability details, minimum requirements, vulnerability techniques, vulnerability chaining with other vulnerabilities, recommendations, practice labs, and much more.

PHP’s popularity has been consistent since its beginning, and statistics show that it is a preferred language for many web application developers. This is one of the reasons why PHP has long piqued the curiosity of the information security community. Another reason PHP is well-known in the information security field is because of its use in various Content Management Systems (CMS), the source codes for which are publicly available on platforms such as GitHub. Since the source codes are open source in nature, several vulnerabilities have been uncovered that are particular to applications and systems that use PHP as their preferred language.

The variety and number of vulnerabilities discovered in PHP-based applications are significant. However, not all of the discovered vulnerabilities are easy to comprehend and detect, and hence the majority of security professionals and developers overlook or ignore those vulnerabilities. PHP Object Injection is one such vulnerability that falls into this category, which we will discuss in this blog so that the reader has a better understanding of this obscure vulnerability, why it exists, and how an attacker might exploit it. In addition, we have created a compilation of four practice labs on our platform Vulnmachines where you may navigate and gain hands-on experience with various real-life scenarios.

TL;DR

  1. PHP Object Injection occurs as a result of the deserialization of data from insecure sources, such as user-supplied input, and the absence of integrity verification measures.
  2. PHP Object Injection makes use of magic methods, which are predefined PHP methods that help in the exploitation of this vulnerability.
  3. Several vulnerabilities such as Command Injection, Path Traversal, Authentication Bypass, and others can be chained with PHP Object Injection to enhance the impact.
  4. An intentionally vulnerable testing playground is available on Vulnmachines for hands-on practice.

Serialization and the serialize() method

Serialization is the process of transforming an object into a value representation to store it in a memory buffer or transfer it to a database, via a network, or to a file. When serialization is performed it is ensured that the state of the object is also maintained, which implies that the properties of the object, as well as their allocated values, are retained. The primary goal of serialization is to store an object’s state, so that it may be recreated when needed.

The ‘serialize()’ method in PHP accepts a single parameter which is the data we want to serialize and converts it into a storable representation of value that can be returned by the method as a serialized string. Other than resource-type data, such as references to external resources and certain objects, the ‘serialize()’ method can handle all types of values. 

The table below outlines the basic structure of a PHP serialized string, which will assist in understanding the serialized strings explained in the subsequent code samples throughout the log post.

Annotation

Description

O:4

Represents an object with the length of class name as 4

s:4:”test”

Represents the parameter name as a string of 4 characters i.e., “test”

a:2

Represents an array of 2 elements

i:2

Represents an element at index two

b:1

Represents a Boolean

d:1.1

Represents a Float

The following table summarizes the technical details associated with the serialize() method:

Syntax

serialize(value)

Parameter Value

Mandatory

Return Type

String

Return Value

A String of byte-stream representation of the specified parameter value, which could be stored anywhere.

Example:

The preceding code snippet creates a ‘SerializationExample’ class object and then generates the serialized string representation of that object:

O:20:”SerializationExample”:1:{s:4:”data”;a:3:{i:0;s:3:”The”;i:1;s:6:”SecOps”;i:2;s:5:”Group”;}}

A better understanding of the above-serialized string representation of the object can be obtained from the below table:

Annotation

Description

O:20

Represents an object with the length of class name as 20

s:4:”data”

Represents the parameter name as a string of 4 characters i.e., “data”

a:3

Represents an array of 3 elements

i:0

Represents an element at index zero

Deserialization and the unserialize() method

Deserialization is the process of reassembling an object or data structure from a single serialized variable into a PHP value.

When a serialized string containing an object and its properties is passed to the ‘unserialize()’ method, the ‘unserialize()’ method will perform Object Instantiation, which is the process of recreating an instance of the provided initially serialized object in memory. Once the object has been recreated in memory, PHP will attempt to invoke the ‘__wakeup()’ magic method and execute the code in the ‘__wakeup()’ method; if a ‘__wakeup()’ method exists, the code will be performed in the existing method.

The ‘__wakeup()’ magic method can recreate any properties that the original object has had. The ‘__wakeup()’ magic method is meant to be used to restore any database connections that may have been lost during serialization and to execute additional reinitialization operations. When no reference to the deserialized object instance exists, the ‘__destruct()’ magic method of that class is invoked.

Example:

The preceding code snippet recreates the ‘SerializationExample’ class object from the serialized string representation of that object.

The following table summarizes the technical details associated with the ‘unserialize()’ method:

Syntax

unserialize(string, options)

Parameter String

Mandatory (Serialized String)

Parameter options

Optional (Could be an array of class names, false for no classes and true for all classes)

Return Type

String, Boolean, Array, Float, Integer, or Object

Return Value

The unserialized value

What is PHP Object Injection?

When the user-supplied input is not properly validated and sanitized before being sent to the ‘unserialize()’ PHP function, PHP Object Injection occurs. Since PHP supports object serialization, attackers could supply arbitrary serialized strings to a susceptible ‘unserialize()’ method, resulting in the injection of arbitrary PHP object(s) into the application. PHP Object Injection allows for the unrestricted alteration of object content, which must be unserialized using the PHP ‘unserialize()’ function.

Prerequisites

The following prerequisites must be satisfied in order to properly exploit a PHP Object Injection vulnerability:

  • On the injection points, the application must not have a comprehensive input validation mechanism.

  • The application must not validate the serialized data’s integrity and must enable deserializing data from untrusted sources.

  • A class that implements at least one PHP magic method must be present in the vulnerable application (In the parts that follow, there will be further information about these).

  • All of the classes used during the attack must be declared when the vulnerable ‘unserialize()’ is being called, otherwise object autoloading must be supported for such classes.

Impact

The impact of PHP Object Injection depends largely on the context where it is discovered; however, it enables an attacker to execute a variety of malicious attacks, including but not limited to Code Injection, SQL Injection, Path Traversal, and Application Denial of Service, among others.

Since we now understand what PHP Object Injection is and what prerequisites must be satisfied for it to exist, and what impact it has, let us now look at the showstopper, i.e., the magic methods, which play a critical part in the existence of this exotic vulnerability.

Magic Methods

PHP magic methods are methods that are automatically invoked when specific criteria are satisfied. As they are predefined methods, one cannot have functions with these names in any of the classes unless users desire the accompanying magic functionality. A magic method is easily distinguished from conventional methods since it begins with a double underscore in PHP.

PHP includes 15 magic methods that can lead to PHP Object Injection, all of which are listed in the table below:

Magic Method

Details

__call()

When a non-existing or inaccessible method is called by an object, PHP will automatically invoke the ‘__call()’ magic method using the name of the method you requested to call and the argument you provided as parameters.

The below-mentioned code snippet utilizes the ‘getAnyRandomData’ method, which is not declared anywhere in the above code snippet, and therefore the ‘__call()’ magic method is invoked.

__callStatic()

When an inaccessible static method of a class is called by an object, PHP will automatically invoke the ‘__callStatic()’ method.

The below-mentioned code snippet utilizes the ‘getAnyRandomData’ static method, which is not declared anywhere in the above code snippet, and therefore the ‘__callStatic()’ magic method is invoked.

__clone()

When an object is cloned in PHP with the clone keyword, the ‘__clone()’ magic method is invoked. After the object has been cloned, the ‘__clone()’ magic method is used to manipulate the object’s state.

The below-mentioned code snippet utilizes the ‘clone’ keyword as a result of which PHP invokes the ‘__clone()’ magic method.

__construct()

Due to the fact that it is used to configure a class when it is initialized, PHP’s ‘__construct()’ method is the most frequently used magic method. Classes with a constructor method utilize this method on newly formed objects, making it ideal for any initialization that the object may require before it is utilized. Classes with a ‘constructor’ method utilize this method on newly formed objects, making it ideal for any initialization that the object may require before it is utilized.

The below-mentioned code snippet utilizes the ‘__construct()’ magic method which is invoked by PHP when the object of the ‘constructMagicMethod’ class is created.

__debugInfo()

The ‘__debugInfo()’ magic method is executed when an object is utilized inside the ‘var_dump()’ method to dump information for debugging purposes. The ‘__debugInfo()’ magic returns an array of variables that might be helpful while performing debugging.

The below-mentioned code snippet utilizes the ‘__debugInfo()’ magic method which is invoked by PHP when the object of the ‘debugInfoMagicMethod’ class is used inside the ‘var_dump()’ method as a parameter.

__destruct()

PHP automatically invokes the ‘__destruct()’ magic method when the object is destroyed at the end of the program and no longer serves usage.

The below-mentioned code snippet utilizes the ‘__destruct()’ magic method which is automatically invoked by PHP when the object of the ‘destructMagicMethod’ class is destroyed at the end of the program and no longer serves usage.

__get()

The ‘__get()’ magic method is invoked when an inaccessible private or protected variable, or a variable that does not exist, is used.

The below-mentioned code snippet utilizes the ‘__get()’ magic method inside the ‘getMagicMethod’ class which is invoked by PHP when a non-existing variable is used.

__invoke()

When an object is treated as a function, the ‘__invoke()’ magic method is called. The major function of the ‘__invoke()’ method is that you may implement it if you wish to represent your objects as callable.

The below-mentioned code snippet utilizes the ‘__invoke()’ magic method which is called automatically by PHP when the object of the ‘invokeMagicMethod’ class is treated as a function.

__isset()

The ‘__isset()’ magic method is triggered when a call is made to the ‘empty()’ or ‘isset()’ method on an inaccessible or non-existent object property.

The below-mentioned code snippet uses the ‘isset()’ function on the Test property, which is absent from the ‘issetMagicMethod’ class, and thus the ‘__isset()’ magic method is triggered.

__set()

When an inaccessible variable, such as a private or public variable, or a non-existent variable, is edited or altered, PHP automatically invokes the ‘__set()’ magic method.

The below-mentioned code snippet utilizes the ‘__set()’ magic method inside the ‘setMagicMethod’ class which is invoked automatically by PHP when a non-existing variable i.e., ‘$absentVariable’ is used or altered.

__set_state()

The ‘__set_state()’ magic method is a static method that works with the ‘var_export()’ function. When using this magic method to export classes, the ‘var_export()’ function must be specified in the class.

In the below-mentioned code snippet PHP automatically invokes the ‘__set_state()’ magic method when the object of the ‘setstateMagicMethod’ class is passed to the ‘var_export()’ function.

__sleep()

The ‘__sleep()’ magic method is triggered when the ‘serialize()’ function is invoked on an object. Specific properties are saved during serialization under certain cases, and the ‘__sleep()’ magic method returns an array containing the names of properties that must be serialized.

The below-mentioned code snippet utilizes the ‘__sleep()’ magic method which will be invoked when the object of the ‘sleepMagicMethod’ class will be passed to the ‘serialize()’ function and only the properties i.e., ‘firstName’, ‘middleName’, and ‘lastName’ will be preserved.

__toString()

When an object is represented or processed as a string, PHP automatically invokes the ‘__toString()’ magic method. Essentially, this magic method assists in expressing what must be showcased when the object is processed as a string. If the ‘__toString()’ magic method is not available and the class object is used by ‘print’ or ‘echo’, an error message will be displayed.

The below-mentioned code snippet utilizes the ‘__toString()’ magic method, which is invoked automatically by PHP when the object of the ‘toStringMagicMethod’ is passed to ‘echo’ and therefore treated as a string.

__unset()

PHP invokes the ‘__unset()’ magic method when an ‘unset()’ function is used on an inaccessible private, protected, or non-existent object property.

The code snippet below invokes the ‘unset()’ function on the ‘Test’ property, triggering the ‘__unset()’ magic method, which restores the ‘Test’ property’s original state.

__wakeup()

When the ‘unserialize()’ function is invoked on the object, the ‘__wakeup()’ magic method is intended to re-establish any connections that may have been lost during serialization and perform other reinitialization tasks.

The code snippet below utilizes the ‘connectionmethod()’ function, which is used by the ‘__wakeup()’ magic method, to re-establish the connection when the ‘unserialize()’ function is implemented to the ‘wakeupMagicMethod’ class object.

PHP Object Injection Samples

As we understand what PHP Object Injection is and how insecure deserialization and magic methods in PHP are the underlying cause, let us look at some vulnerable PHP Object Injection samples to get an understanding of what PHP Object Injection can lead to.

Authentication Bypass via Object Reference

The following example demonstrates the PHP class ‘POI2IDOR’ with an exploitable ‘unserialize()’ method. In line 9 of the code snippet below, we observe that the data from the ‘inputparameter’ is deserialized in an unsecured way, which is then given to an ‘if’ conditional statement on line 12 that is subject to Type Juggling. When a specifically designed serialized object is supplied in this example, an attacker might be able to obtain an object reference for accomplishing an authentication bypass, for example, by accessing the following URL: ‘http://vulnerable.target.com/test.php?inputparameter=a:2:{s:15:”adminsecretcode”;N;s:4:”misc”;R:2;}’

Vulnerable Code Snippet:

Path Traversal

The below-mentioned example demonstrates a PHP class ‘POI2PT’ with an exploitable ‘__destruct()’ magic method spanning from line 5 to line 10. When a specifically constructed serialized object is supplied in this example, an attacker might be able to perform Path Traversal, by browsing the following URL: ‘http://vulnerable.target.com/test.php?inputparameter=O:6:”POI2PT”:1:{s:12:”existingfile”;s:22:”../../../../etcpasswd”;} ‘

Vulnerable Code Snippet:

Authentication Bypass via Type juggling

The following example demonstrates an exploitable unserialize method. When a specifically constructed serialized object is supplied using a POST request, an attacker will be able to perform an authentication bypass with the help of type juggling as the comparison statement on line 5 appears to be loosely comparing the data. The HTTP request for exploiting this misconfiguration will be similar to the one mentioned below:

Vulnerable Code Snippet:

POST Request:

Arbitrary Code Injection

As seen in the following example, the PHP class ‘POI2CI’ includes an exploitable ‘__destruct()’ magic method from line 7 to line 9. When a precisely configured serialized object is supplied in this code snippet, the application will seek references to the ‘receivedinput’ object, and once there are no more references, the ‘__destruct()’ magic method will be called, resulting in the execution of the injected command.

The attacker will be able to obtain command injection, by accessing the following URL: ‘http://vulnerable.target.com/test.php? inputparameter=O:6:”POI2CI”:1:{s:16:”arbitrarycommand”;s:2:”id”;}’

Vulnerable Code Snippet:

PHPGGC: PHP Generic Gadget Chains

PHPGGC is a deserialization payload library that includes a tool for generating them from the command line or programmatically. This may be used to produce Property Oriented Programming (POP) gadgets from libraries that users have already discovered. While facing unserialize on a website for which you do not have the source code, or simply when attempting to develop an attack, this tool allows you to produce the payload without having to go through the time-consuming procedures of locating and assembling gadgets.

CodeIgniter4, Doctrine, Drupal7, Guzzle, Laravel, Magento, Monolog, Phalcon, Podio, Slim, SwiftMailer, Symfony, WordPress, Yii, and ZendFramework are all supported by PHPGGC. Additionally, PHPGGC requires a PHP version greater than or equivalent to 5.6 for proper functioning.

The attributes associated with a gadget chain generated by the PHPGGC library are described in the table below:

Attribute

Description

Name

Name attribute specifies the framework or library that the gadgets are intended for.

Version

Version attribute specifies the framework or library version for which the gadgets are intended.

Type

Type attribute describes the sort of exploitation the gadget chain would result in, such as Remote Code Execution, Arbitrary File Deletion, Server-Side Request Forgery, and so on.

Vector

Vector attribute describes the PHP magic method that will be used to initiate the chain following the unserialize operation on the specified input.

Information

Information attribute describes any extra detail associated with the gadget chain.

At the time of writing, the PHPGGC library has 94 gadget chains associated with various frameworks and libraries that it could exploit. The list of available gadget chains in PHPGGC can be obtained by executing the below-mentioned command:

Depending on the application technology, we can also use the following command to filter the gadget chain available in the PHPGGC that is most suited for testing:

PHPGGC provides a ‘-i’ flag that can be used to retrieve additional information about the specified gadget chain using the command below:

On a high level, PHP Object Injection Exploitation Using PHPGGC includes the following activities:

  1. Detection of the framework or library in use.

  2. Identification of a possible injection point, such as a parameter, cookie, or Header.

  3. Additional attack preparation, such as a reverse shell or a backdoor, which will be uploaded to the application server upon successful exploitation of the vulnerability.

  4. Custom serialized object creation using PHPGGC

Interesting Real World Scenarios

Over time, security experts worldwide have discovered several instances of PHP Object Injection vulnerability, some of which could be exploited to achieve Remote Code Execution, Authentication Bypass, etc. Some of the most significant cases of the PHP Object Injection vulnerability are described below, which will aid in acquiring a better understanding of this vulnerability and knowing some of the finest publicly known exploits.

Data Tampering through PHP Object Injection

WooCommerce is a WordPress e-commerce plugin. It simplifies the process of developing and administering an online store by providing decent levels of flexibility as well as various important features such as inventory and tax administration, secure payments, and shipping integration.

In this case, the security researcher was testing the order feature in the WooCommerce WordPress plugin and discovered that the PHP native unserialize method allows small ‘o’ as an option and treats it as a ‘StdClass’ object.

Vulnerable Code Snippet:

Using the obtained knowledge about the small ‘o’ as an option, the security researcher constructed a bypass that allowed them to tamper with any ‘order’ object. In addition, when paired with capital ‘S’, existing firewall rules failed, causing the firewall to be bypassed.

Proof of Concept Code:

Type Juggling Chained with PHP Object Injection for SQL Injection

ExpressionEngine is a robust and flexible open-source Content Management System. ExpressionEngine has a large community of developers making add-ons that extend system capabilities.

In this scenario, when the security researcher attempted to log in with the username ‘admin,’ the server responded with a cookie containing PHP serialized data. The researcher then downloaded and installed a copy of the application for further analysis. After walking through the code, the researcher discovered that a few checks were done before the cookie was parsed and subsequently unserialized. Further investigation revealed that a cookie integrity validation mechanism was in place, ensuring that the cookie was not tampered with. Since the application was written in PHP, a loosely typed language, using the loose comparison operator resulted in the comparison evaluating as true because ‘0ex’ will always be zero when cast to integers, a process known as Type Juggling.

The researcher then created the following proof of concept, which brute forces the ‘md5($payload.$this->sess_crypt_key)’ until an MD5 hash sum of the updated ‘$payload’ and ‘$this->sess_crypt_key’ that starts with ‘0e’ and ends in all digits was discovered:

Proof of Concept Code:

Output:

When the researcher compared the MD5 hash sum to any ‘$signature’ value, they discovered that the original ‘$payload’ was successfully updated, confirming the existence of Type Juggling and PHP Object Injection.

The researcher discovered a method that appears to be checking the database to see if the given ‘$username’ is locked out pre-authentication. Since the researcher got access to the value of ‘$username’ and he was able to inject a SQL query there, it resulted in SQL injection.

Following an additional inspection of the source code and multiple hit-and-try efforts, the researcher constructed the Proof of concept code shown below to insert the ‘sleep(5)’ command into the database:

Proof of Concept Code:

 

Output:

The researcher then submitted the below-mentioned payload through the ‘exp_flash’ cookie generated by the preceding proof of concept code and observed that the response was shown after 5 seconds, confirming the vulnerability:

Object Injection to Pre-Auth Remote Code Execution in Moodle

Moodle is a learning platform that aims to give educators, administrators, and learners a unified, secure, and integrated system for creating personalized learning environments.

In this scenario, the researcher discovered that Moodle’s Shibboleth authentication module was vulnerable to Remote Code Execution through a PHP Object Injection vulnerability. The vulnerability was discovered in the logout handler in the ‘logout_file_session’ function spanning from line 17 to line 22, since the file handler traverses all session files. Each session file holds a single serialized session object. The handler handles this by reading each file and deserializing its data using the ‘unserializesession’ function. When a matching session ID in a session object is identified, the relevant file is unlinked, resulting in a ‘logged-out’ session, as shown in the below-mentioned code snippet:

The code fails to properly parse the serialized value of a session key if it contains an unexpected Pipe ‘|’ character, introducing the risk of the parser misinterpreting the beginning and end of a serialized value within the session object, leading to the deserialization of potentially user-controlled input.

The researcher sent the payload ‘xxx|O:8:”Evil”:0:{}’ which was saved in the ‘$_SESSION’ object of an unauthenticated guest user, which the session handler then serialized and stored on the filesystem in the form ‘USER|O:8:”stdClass”:…:{…} SESSION|O:8:”stdClass”:…:{… s:…:”filterfirstname”;s:19:”xxx|O:8:”Evil”:0:{}”;…}’, confirming the PHP Object Injection vulnerability which was then exploited for obtaining Remote Code Execution.

Practice Labs

Being a team of Advanced Penetration Testers and Security Researchers, we strongly believe in a hands-on approach to cyber security and therefore we have published a compilation of four PHP Objection labs on our platform Vulnmachines for the readers to learn PHP Object Injection by practicing in labs that mimic real-life circumstances. 

The practice labs relevant to PHP Object Injection are listed below and can be accessed by navigating to Vulnmachines:

  • Demystifying PHP Object Injection (Why So Serialize)

Mitigations and Best Practices

  • Deserializing user input should be avoided unless necessary.

  • When deserializing data from untrusted sources, avoid using the ‘unserialize()’ method with user-supplied input and instead use the ‘json_decode()’ and ‘json_encode()’ methods.

  • Implement a verification mechanism for serialized data before deserializing it from any untrusted source, such as user input.

References