Simple Machines Forum is a popular web community software written in PHP. Its password reset mechanism had some issues back in 2008. The vulnerability was caused by insecure random numbers generation and was patched right after the exploit was published. This time I found a more interesting vulnerability however at Positive Technologies we didn’t manage to raise awareness of SMF developers. After continuous silence of SMF devs I am disclosing the vulnerability details but I am not going to publish the exploit.
The new vulnerability in password reset algo persists in SMF since the early versions and derives from insecure string handling and PHP weird type casting. The attack scenario based on incorrect string comparison was first described by Omar Ganiev and was applied to phpBB forum. Despite the fact that the actual phpBB code made it infeasible to conduct the attack the core idea is rather interesting. Imagine a web application that generates password reset token that contains only hex symbols (a-f, 0-9) and is rather short. Now let me remind you about PHP type juggling pecularities. If you use == operator to compare strings that contain numbers they will automatically be casted to integers, for example a string “123” will be equal to an integer 123. But what is more interesting is the following abstract from PHP documentation on type juggling:
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). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an ‘e’ or ‘E’ followed by one or more digits.
It means that if you compare a string “0e1337” with a string “0” they will be equal, because 0 * 10 power 1337 is zero. Not so many web developers are aware of this kind of unexpected magic, however it leads to a potential security hole. If an attacker starts to send password reset requests and iteratively tries to match it “0” or “1”, one lucky request will let him set admin password.
So, what about SMF? It is an ideal example of one insecure piece of PHP code. Just see this:
if (empty($_POST['code']) || substr($realCode, 0, 10) != substr(md5($_POST['code']), 0, 10))
Lack of !==, only 10 symbols and hex format. Excellent! As you can see $_POST[‘code’] is md5’d, so we need to find such values that will give us 0e[num] or 1e[num]. These values are:
md5(5136) == 0e79548081b4bd0df3c77c5ba2c23289
// md5(8301) == 1e79596878b2320cac26dd792a6c51c9
// 1eXXXX will never give us 1 except 1e0, because 1eX = 1 * 10 power X (thx to gifts and Paul4games)
However newer versions of SMF has a limitation of password reset rate to prevent bruteforce:
if (empty($number_tries) || $time_stamp < (time() - 10))
{
// If it wasn't *that* long ago, don't give them another five goes.
$number_tries = !empty($number_tries) && $time_stamp < (time() - 20) ? 2 : 0;
$time_stamp = time();
}
There is another bug. If an attacker sends a password reset request within 10-20 second interval the $number_tries will be set to 0 instead of 2. Obviously this was not SMF devs intention.
The successful attack requires about 6-10 k requests and can be done in a night. The older versions of SMF do not have this limitation, so you will be able to set new password in few hours.
Leave a Reply