Cookie Forgery Part-2

Furqan Ahmed Khan
By Furqan Ahmed Khan

January 18, 2017

Last time we discussed the prevalence of cookie forgery one of the ways if can be prevented. Considering a situation where there is a flaw in the cookie formation and cryptography application, we solved this by introducing a delimiter between the username and expiration. Today we’ll discuss what it means when there’s a flaw in the comparison logic in server side code, and how to fix it.

When a hacker tricks an application’s hash comparison logic to accept invalid cookies, he is able to impersonate a genuine user. If we look at how this is done, we can easily come up with a solution on how to prevent it.

Flaw in the comparison logic at server side code

The doorway to this hack lies in the use of a non-strict comparison in the WordPress cookie validation code. This allows a hacker to forge authentication cookies by exploiting the PHP’s type-juggling. Assuming the system possesses the fix laid out in part one, a weak comparison logic can still allow a hacker to impersonate other users.

Here’s an example of a comparison logic flaw:

if ( $HMACfromcookie != $hashatserver ) { // invalid cookie }

  • $user = get_userdatabylogin($username);

The issue with this non-strict bug is that the MAC verification is bypassed via the PHP’s loose string comparison. By loose string comparison we mean that WordPress is written in PHP, which has a concept of type-juggling that converts an underlying string to its numeric value when a comparison is done in numeric context. Here’s an example:

  • var_dump('1abcdef' == 1); // true , var_dump('2abcdef' != 2); // false
  • var_dump('abcdef' == 0); // true , var_dump("000000e1234" == "0"); // true

Note that the ‘e’ denotes the multiplication of ten to the power of the number that follows; e.g. 2e1 is 20. All of the examples above are equal because 0 multiplied by anything is still 0. As per the PHP string comparison, when a string is evaluated in a numeric context, the resulting value and type are determined as follows:

  • If the string does not contain any of the characters '.', 'e', or 'E' and the numeric value fits into integer type limits (as defined by PHP_INT_MAX), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
  • The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero).

In the above example we are making use of a non-strict PHP comparison operator. The idea here is to generate a cookie for a registered user which would generate a zero-like MAC.

We know that the MAC is generated by:

$hash = hash_HMAC ('md5', $username. '|' . $expiration, $key);

In this function for a given user the only thing that changes every time a user may login is the $expiration (expiration time of cookie) .The trick therefore is to keep trying login attempts until such time as the generated Mac is at zero-type. The reason we are emphasizing a zero type MAC is because it is so easy to bypass it once it reaches that point. A zero type MAC can refer to something like: “0e768261251903820937390661668547”; and when compared to string “0”, it will return as valid:

(0 * 10^30) =0.

In order to bypass the server authentication, an attacker can place value “0” inside the HMAC portion of the cookie, rendering the rest of the data as a valid authentication for USERNAME “john”.

The USERNAME “john” HMAC_FUNCTION ("john|1209331305", $key) might produce an HMAC like:


Thus a valid cookie is thus generated:


But due to non-strict comparison, a cookie like john|1209331305|0 would bypass the comparison performed at the server and would authenticate “john” with valid session.

  • if ( $HMAC != $hash ) { // invalid cookie } will return false as :
  • (“0” !=” 0e7682612519038209373906616”8547") –False (PHP loose string comparison)
  • $user = get_userdatabylogin($username); (will get executed)

Some rough math suggests that the average number of requests required to hit a zero-like MAC is just over 300,000,00—even if the attacker knows the authorization key used at the server.

So what’s the fix?

Both examples discussed in part one & two fall under [Option 2] of cookie/session ID generation & handling. These focus on saving the HMAC value within the cookie and generating the HMAC from the username, expiration and keys.


Option 1 suggests using random operating system values and storing the cookie value within the database. Although this approach is relatively safer, there are still ways to exploit it—if proper security measures are not adapted. PHP’s session ID generation is similar to [Option 1], but can still be exploited.

The best security practice is to always make sure proper encryption, hashing and randomness are incorporated while generating session Ids. This makes it impossible to "hit" a session identifier when trying random ones. Furthermore, the authentication logic that would validate a cookie must be secure, and should not use loose comparison operators.

For more details on cookie forgery, check out the following links:

Tags: Uncategorized