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

Leave a Reply

Your email address will not be published. Required fields are marked *

Arrange a Callback

    Contact us
    Close