“You reached Free Parking” - sadly we ran out of money, but here have a free flag instead.

Category: Web

Solver: Liekedaeler

Flag: GPNCTF{just_php_d01ng_php_th1ng5_abM2zz}

Scenario

While this web challenge does not come with downloadable source code on the challenge platform, you’ll be greeted by its source code as soon as you navigate to it. It’s just a few lines of PHP, so we don’t have many options. All we can do is post a string to it.

Analysis

This is the source code we are given:

<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    highlight_file(__FILE__);
    exit;
}

$flag = getenv("FLAG");
$guess = file_get_contents('php://input');

//Check if user knows the flag
if (md5($flag) == md5($guess)) {
    echo("You correctly guessed the flag - " . $flag);
} else {
    echo("You guessed wrong: The flags hash is " . md5($flag) . " and the hash of your guess is " . md5($guess));
}
?>

If we do anything but a POST request, we are presented with the source code. If we do a POST, it takes our data, MD5 hashes it and compares that to the MD5 hash of the flag. The crux of this challenge lies in the comparison. PHP knows two types of comparisons: == (“equal”) and === (“identical”). They have one notable difference: === ensures that both elements are of the same type while == only checks whether both elements are equal after type juggling.

As the website discloses the flags hash, let’s take a look at it:

$ curl -X POST https://elmside-of-indomitable-unity.gpn23.ctf.kitctf.de -d 'foo' 
You guessed wrong: The flags hash is 0e457091929243384888029339511631 and the hash of your guess is acbd18db4cc2f85cedef654fccc4a4d8

We notice that the flags hash looks suspiciously like a number in scientific notation. Obviously, that doesn’t matter, because we’re dealing with strings for now. But the type juggling now does something interesting - it casts the string to a float, because, after all, it looks like a float. If we take a look at the result of that (by running php -r 'echo (float)"0e457091929243384888029339511631";'), we see it prints out 0. And that is exactly where the problem lies. Now all we need is an input whose md5 hash is also 0e<numbers>. These are known as magic hashes (yes, they even have a name). If you google that, you’ll stumble upon tons of them. Even simply googling something related to php type juggling exploits will get you there. For our purposes, we’ll use 240610708. If we hash that, it indeed fulfills our criteria:

php -r 'print(md5("240610708"));'
0e462097431906509019562988736854

The type juggling will now also convert our hash to a float, meaning it becomes 0 as well. 0 == 0 is true and we pass the check to get the flag. Now all that is left for us to do is submitting our input:

curl -X POST https://elmside-of-indomitable-unity.gpn23.ctf.kitctf.de -d '240610708'
You correctly guessed the flag - GPNCTF{just_php_d01ng_php_th1ng5_abM2zz}