The solution
There are 2 problems with the authorisation script - the reason the script is vulnerable is because both problems exist at the same time.
We 'll see them one by one.
Not correctly comparing values
PHP:
$cachedCredentials['username'] == $user
When is the above comparison true? Obviously when you have "admin" on the left and "admin" on the right (or any other string on both sides).
But what happens if you have "admin" (string) on the left and 5 (an integer) on the right? Well, 5 and and "admin" are obviously not the same and it should fail (and it does - live example:
http://codepad.org/ZhSVay6y )
Ok, no problem so far - but what happens if you have "admin" on the left and 0 (integer) on the right? Well, they are different so we expect the equality comparison to fail.
Wrong! The comparison is true! (live example:
http://codepad.org/Y41y6Zey)
What did just happen here? Why does PHP think that "admin" and 0 are equal? Is it stupid?

As usually, we should read the f*cking manual:
http://www.php.net/manual/en/language.operators.comparison.php
$a == $b | Equal | TRUE if $a is equal to $b after type juggling
Type juggling? Yes - PHP assumes we are intelligent people and when we compare apples ("strings") with oranges ("integers"), we do it on purpose. And since we do it on purpose, PHP does the casting of oranges to apples for us transparently.
Reading about type juggling in the
manual, it says that "
a variable's type is determined by the context in which the variable is used". In other words, if I have apples on the left and oranges on the right, PHP will convert apples to oranges first and THEN perform the comparison between the two bags of oranges.
Indeed, we can see that "admin" when cast to integer, it returns 0 (http://codepad.org/xQ9OH9ak)
So, all we need to bypass the authentication, is to fill the $user and $pass variables with 0. Which leads us to the second issue...
serialize/unserialize is evil
Serialize() is a simple way of transforming an object to a string in a way that the object can be "rebuilt" later, in order to save it somewhere (a file, a db, a cookie). When you want to get the object back, you retrieve the string, pass it to unserialize() and voila - your object as you left it.
When you retrieve a value stored in a cookie, it is always a string. Given this fact, we could never exploit the problem described above by simply putting "0" value in the cookie. But because the script used the serialize/unserialize method, we now can!
Reading the
manual, we see that unserialize() takes a string and returns mixed (i.e. various valid language types). Now, we can construct a string that after being read from the cookie, it will hold integer values instead of strings.
So, Brain only need to hack his cookie to have this string:
Code:
a:2:{s:8:"username";i:0;s:8:"password";i:0;}
Indeed, we can see it 's working:
http://codepad.org/kw1pOpHV
How do we construct that funky string? We can ask PHP to do it for us
PHP:
$z = array( 'username' => 0, 'password' => 0);echo serialize($z);
Prevention
Since the script uses the serialize/unserialize method, the programmer should have used the === operator instead of the == . The === does not do type casting, so if you compare apples with oranges, the comparison will always fail.