Type Juggling and Dangers of Loose Comparisons


Welcome back,
It’s Hay here with another important topic.

Today, I want to discuss about a vulnerability that is rarely talked and often stays under the hood, yet represents a significant security issue once it’s found - ‘Type Juggling’ Vulnerability:

type_juggling_wtf

For a web application to function correctly, it needs to perform various comparison and calculation checks on the backend. These include authorizing users based on their relevant privileges, managing a password reset mechanism for users who have forgotten their passwords, validating sessions to authenticate users, and such on.

All the examples mentioned above require the use of comparison statements to achieve their functionality properly. Attackers who understand this potential may attempt to bypass these mechanisms to lead to unexpected results.

TL;DR

Programming languages like PHP support ‘loose comparison’ operators (==, !=) that interpret equality differently in if statements. This can lead to security bypass issues and present risks to the entire application.

Make sure to check and compare both the value and their type to ensure the comparison is based on strict (===, !==) comparison.

Note: In PHP versions newer than PHP 5, this issue has been resolved.

What ‘Loose Comparison’ is all about?

In languages like PHP, JavaScript, and Ruby, comparison operations are based on the values of variables rather than their types, which is known as ‘loose’ comparison.

This approach can lead to issues in certain cases, unlike ‘strict’ comparison where both value and type must be matched.

PHP Comparision Table:

To illustrate the differences between loose and strict comparison types, PHP.net 1 presents various use cases scenarios that highlight the importance of using the correct comparison operator to get the right outcomes:

php_loose_comparison_table

Loose comparisons table

Versus:

php_strict_comparison_table

Strict comparisons table

Some unexpected examples which yields True in loose comparison, whereas it yields False in strict comparison:

  • "php" == 0
  • "10 foxes on the tree" == 10
  • 0e13466324543662017 == 0e5932847

Wait, 0e123456789012345 == 0e987654321012345, seriously??

Yes, you are not wrong 😃

In ‘Type-Juggling’, strings that start with “0e” followed by digits (like “0e13466324543662017” or “0e5932847”) are considered equal to zero (0) in ‘loose comparison’.

Consider these examples:

  1. var_dump(“0e13466324543662017” == “0e5932847”); // bool(true)
  2. var_dump(“0e13466324543662017” == 0); // bool(true)
  3. var_dump(“0e5932847” == “0”); // bool(true)

This case study can play a significant role when we want to bypass comparison checks if we have control over the parameters in the equation.

MD5 Attack Scenario:

Let’s take a look at a code snippet responsible for validating the authenticated user’s cookie to grant them the appropriate privileges on the web application:

vulnerable_cookie_validation

From the attacker’s perspective, we can see that the function receives the cookie from the user’s side, which consists of three parts:

  1. Username cookie
  2. Token cookie
  3. Date Expiration cookie

We have control over the username and expiration cookie values, while the token is pulled from the database. We do not know its value because we do not own the ‘Admin’ account.

On line 14, we can see the ‘loose comparison’ operator (==), which hints at a Type-Juggling vulnerability. Let’s find a way to exploit this check to impersonate the ‘Admin’ account.

So, if we follow the rule that “0e[0-9]{10}” == “0” (pay attention to the substr in the snippet code - we need only 10 first digits match), we can make our equation evaluate to TRUE and be authenticated.

Let’s examine the following flow:

If we set “0” as the value for $cookie_token cookie and control $final_token to return a string in the format of “0e..”, we’ll be successful. But how do we get $final_token to be starting with “0e” when we only control $cookie_expiration?

The answer: Brute force technique!

The attack will require brute-forcing $cookie_expiration values until the final $final_token value begins with “0e” followed by only digits. Since we do not know the $user_token value at this point, an ‘Online Brute Force’ attack is necessary here.

I’ve developed a short Python PoC code to demonstrate that:

brute_force_python_script

The final HTTP request payload will look like this:
cookie: username=admin; token=0; expiration=1858339652;

Take into consideration that the expiration value will be different for each user depending on his $user_token value.

NULL == 0 - Oh no, Strikes Again??

Let’s take another example, but this time we’ll focus on the ‘strcmp’ function, which compares two different strings to find a match between them:

strcmp_scenario

As you can see, the function ‘login’ is receiving the user and pass arguments from the client side. It then pulls the password for the account directly from the database and compares the pulled password to the provided one using the ‘strcmp’ PHP built-in function.

So, in order to bypass this check, we need to figure out the correct password for the ‘admin’ account that we want to impersonate.

Meanwhile, on PHP.net…

While looking at the ‘strcmp’ documentation on PHP.net, we noticed some user comments warning against using this function due to its potential for ‘extremely unpredictable’ behavior caused by string comparison mismatches in certain circumstances:

php_strcmp_comment

'strcmp' function comments from PHP.net

What we can understand from this comment is that strcmp(NULL, “Whatever_We_Put_In”) will always return ZERO, which leads to a successful string matching and will pass the check!! 😈

So, if we able to find a way to pass a NULL value instead of the secret password, we won.

Based on the PHP.net user comments above, we can infer the following flow:
strcmp(“foo”, array()) => NULL <=> NULL == 0

Note: PHP treats NULL as 0.

If we send an array as the password parameter, PHP will treat it as an empty array, confirming the conclusion above:
https://192.168.1.100/login.php?username=admin&password[]=’’

That is ‘Type-Juggling’ attack, requires some creativity, yet it can result in devastating impact!

Conclusion

This article aims to present high risk vulnerability that we can sometimes find in the wild once we have access to the application’s source code, and may potentially risking the entire application.

This vulnerability is not new, but not many people have heard about it, and discovering it can be a game-changer for the attacker.

For additional information and materials, I highly recommend referring to ‘PayloadsAllTheThings / Type Juggling’ 2 resource.


Thanks for reading!


Disclaimer: This material is for informational purposes only, and should not be construed as legal advice or opinion. For actual legal advice, you should consult with professional legal services.