websec.fr level03

Basically this challenge focuses on this particular line:

$h2 = password_hash (sha1($_POST['c'], fa1se), PASSWORD_BCRYPT);

Note the use of fa1se instead of false, which evaluates to true.


From php.net:

php.net sha1 entry

With raw_bytes enabled, sha1 instead outputs the literal binary form of the hash, instead of the hex encoding. This leads to bad things happening when its piped to a function not expecting raw bytes. If we look into the C representation of password_hash, we see:

PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len)

The password parameter is of type char *password. Strings within C are null terminated, meaning that the end of string is determined by the presence of a null byte. A deeper analysis of this can be found here, but what this essentially boils down to is that the hash we want to replicate 7c00249d409a91ab84e3f421c193520d9fb3674b has been effectively reduced to 7c00.

We can find a string which collides with this short hash through:

    $key = "algjhsdiouahwergoiuawhgiouaehnrgzdfgb23523";
    $i = 0;
    $found = [];

    while (count($found) < 2) {
        $pw =str_repeat($i, 5);
        $hash = sha1($pw, true);
        $hashp = sha1($pw, false);
        if ($hashp[0] == '7' && $hashp[1] == 'c' && $hash[1] === "\0") {
          $found[] = $pw;

    var_dump($i, $found);


carey@devoops in ~
> php lol.php
string(40) "7c00c05b7f7c385abbc7179b783a5a8d959c171d"
string(40) "7c006e7eedae7a4b6c3a591cefee932ae812b046"
array(2) {
  string(20) "83788378837883788378"
  string(25) "9581895818958189581895818"

So by submitting either of thise strings, we end up with a string that hashes to the same hash.

Show Comments

Get the latest posts delivered right to your inbox.