PHP Supply Chain Attack on PEAR

We discussed this vulnerability during Episode 133 on 04 April 2022

Weak entropy in a password reset token, and an archive escape using symlinks to achieve code execution.

The first issue is in how the password reset token is generated.

$salt = md5(mt_rand(4,13) . $user . time() . $pass1);

Basically only two things are not exactly known by the attack, the output of mt_rand(4,13) which will be a value between 4 and 13 (inclusive). And time() which can likely be guessed with reasonable accuracy as this is simply seconds since 1970 (unix epoch) and does not include the miliseconds. So an attack could easily guess all possible reset tokens and gain access to the service.

The second issue is one we’ve covered many times before, a symlink in an archive. Create a symlink in the archive and then write a file to/through it, resulting in a write to a folder outside the archive’s root and hopefully into somewhere the webserver will execute PHP from in this case.

Patch

$random_bytes = openssl_random_pseudo_bytes(16, $strong);
if ($random_bytes === false || $strong === false) {
    $errors[] = "Could not generate a safe password token";
    return $errors;
}
$salt = md5($rand_bytes):

While they had the right idea here, getting some random bytes and using that, the key problem with this patch is that the random bytes are stored in $random_bytes but the value passed into md5 is $rand_bytes which is a variable that doesn’t exist. PHP will offer up a warning but otherwise carry on getting the hash of an empty string.