Форумы Криптографическая атака на SMF

Discussion in 'Уязвимости CMS/форумов' started by n-000, 15 Aug 2008.

  1. n-000

    n-000 Elder - Старейшина

    Joined:
    25 Oct 2006
    Messages:
    90
    Likes Received:
    36
    Reputations:
    5
    Наверное не один из нас сталкивался с траблами когда его веб шелл (залитый неимоверними усилиями) на какой либо ресурс, в данном случае это форум SMF находил и удалял злой дятька АдМиН ?
    Размышляя на эту тему хотел бы боделиться некоторыми соображениями, для которых необходимо иметь веб шелл на этом ресурсе ...
    Сампле машин форум имеет некоторые фишки, как и большое кол во движков (не только форумы), такие как ретрив пароля ... так вот имея доступ к БД, я как то раз зделал ретрив пасса админу и подсмотрел табличку в базе под названием validation_code, формирование этого кода в смф происходит таким образом :

    PHP:
    $password substr(preg_replace('/\W/'''md5(rand())), 010);

        
    // Set the password in the database.
        
    db_query("
            UPDATE 
    {$db_prefix}members
            SET validation_code = '" 
    md5($password) . "'
            WHERE 
    $searchField = '$_POST[user]'
            LIMIT 1"
    __FILE____LINE__);

        require_once(
    $sourcedir '/Subs-Post.php');

        
    sendmail($row['emailAddress'], $txt['reminder_subject'],
            
    sprintf($txt['sendtopic_dear'], $row['realName']) . "\n\n" .
            
    "$txt[reminder_mail]:\n\n" .
            
    "$scripturl?action=reminder;sa=setpassword;u=$row[ID_MEMBER];code=$password\n\n" .
            
    "$txt[512]$user_info[ip]\n\n" .
            
    $txt[35] . ': ' $row['memberName'] . "\n\n" .
            
    $txt[130]);

    Так вот, весь смысл в том, что при ретриве генерица произвольное число, шифруется MD5 и обрезается до 10 символов ...
    Хочу заметить , что эта лабуда сначало пишется в базу в столбец validation_code потом ещё раз шифруется и обрезается, только после этого отправляется на почту юзера !

    На первый взгляд дело дрянь ! Но когда во время моих глумленей удалили ВСЕ мои шелы (скорее всего сервер просто почистили, переустановили скрипты и бекапом вернули базу) мне так не показалось, имея на руках эти 10 символов, начал усиленно думать ...
    Я понял, что шелы не вернуть, а вот validation_code наверняка остался при бекапе и зная 10 символов из целого хэша можно попробывать расшифровать и сменить пасс админу. Параметры для брута были известны, это маленькие латинские и цифры, но 10 символов, как понимаете не одна ноч расшифровки ...
    Но как то я случайно вбил все маленкие символы и о чудо -) пасворд про нашол коллизию под эти 10 символов - Благо эта тузла умеет понимать не полные хэши ))
    Результат получился не особо впечатляющим (2nq3&l7) всё портил символ &, при переходе по ссылке на ретрив пасса (http://site.ru/forum/index.php?action=reminder;sa=setpassword;u=1;code=2nq3&l7) этот символ как бы передавал ещё один параметр скрипту ретрива, тоесть всё что до него проходило, а после отсекалось !
    Зная validation_code в СМФ можно без труда изменить пасс любому юзеру, чей код известен. И тут я решил - а что если подделать HTTP пакет и через POST запрос передать переменную code вместе с этим мать его & !
    Воспользовавшись локальным прокси сервером Odysseus я так и зделал -)
    Для не просвящёных одисей - это такая тузла которая при отправке каких либо данных в инет даёт вам возможность отредактировать их перед отправкой.
    Ну и конечно я радовался как первокласник новому году, когда мне сказали что пасс админа успешно поменян и предложили зайти на форум -))
    Через некоторое время мой хитрозамаскированный шелл снова удалили, скорее всего админ опять зделал бакуп базы и всё переустановил, но ещё при этом апгрейдил форум -) наивно , но он до сих пор не вкурил что дело не в уязвимости скрипта, а бэкдор до сих пор храниться в его базе -)
    Вот таким вот образом я обеспечил себе дверь, через которую можно зайти не один раз, даже при полной чистки сервака, был бы бэкап -) ... удачи вам !
     
    7 people like this.
  2. Elekt

    Elekt Banned

    Joined:
    5 Dec 2005
    Messages:
    944
    Likes Received:
    427
    Reputations:
    508

    Все бы хорошо, если бы не одно НО.
    Видимо тебе попалась древняя версия.
    Ибо в 1.1.4 validation_code состоит из 10 символов, тоесть является неполным хешем.
    С точки зрения криптостойкости - это смешно, поскольку такому урезку будет соответствовать большое кол-во коллизий паролей.
    Но авторы smf поставили видимо на сторону практического нахождения коллизии пароля по неполному хешу,
    потому что лично мне не известен более менее приличный софт для нахождения коллизий по неполному хешу
    (именно хешу, а не паролю - не спутайте с поиском по маске пароля в пассвордпро)

    Щас выяснилось, что _https://hashcracking.info/index.php?1 единственный онлайн сервис, позволющий искать известный пароль по маске неполного хеша.

    Пусть validation_code=d8578edf84
    Тогда ищем по маске как d8578edf84%

    Ну и еще одно маленькое НО: чтобы validation_code присутствовал в бд, необходимо, чтобы юзверь хоть раз запрашивал востановление пароля, до того как вы сняли дамп(и не запрашивал после, ибо тогда validation_code изменится).


    smf_1-1-4

    /Sources/Reminder.php

    PHP:
    function RemindMail()
    {
        
    // Randomly generate a new password, with only alpha numeric characters that is a max length of 10 chars.
        
    $password substr(preg_replace('/\W/'''md5(rand())), 010);

        
    // Set the password in the database.
        
    updateMemberData($row['ID_MEMBER'], array('validation_code' => "'" substr(md5($password), 010) . "'"));

        require_once(
    $sourcedir '/Subs-Post.php');

        
    sendmail($row['emailAddress'], $txt['reminder_subject'],
            
    sprintf($txt['sendtopic_dear'], $row['realName']) . "\n\n" .
            
    "$txt[reminder_mail]:\n\n" .
            
    "$scripturl?action=reminder;sa=setpassword;u=$row[ID_MEMBER];code=$password\n\n" .
            
    "$txt[512]$user_info[ip]\n\n" .
            
    "$txt[35]$row[memberName]\n\n" .
            
    $txt[130]);

    /
    Sources/Reminder.php

    function setPassword2()
    {
        
    // Quit if this code is not right.
        
    if (empty($_POST['code']) || substr($realCode010) != substr(md5($_POST['code']), 010))
            
    fatal_error($txt['invalid_activation_code'], false);
     
    #2 Elekt, 21 Aug 2008
    Last edited: 21 Aug 2008
  3. LEE_ROY

    LEE_ROY Elder - Старейшина

    Joined:
    9 Nov 2006
    Messages:
    450
    Likes Received:
    188
    Reputations:
    26
    как он мог остатсо при бакапе, если снесли все скрипты а базу восстановили?
     
  4. Dr.Frank

    Dr.Frank Elder - Старейшина

    Joined:
    31 Jul 2002
    Messages:
    301
    Likes Received:
    72
    Reputations:
    12
    Ну какбэ
    Code:
    & = %26
    А так +
     
  5. n-000

    n-000 Elder - Старейшина

    Joined:
    25 Oct 2006
    Messages:
    90
    Likes Received:
    36
    Reputations:
    5
    El видимо ты не внимательно читал то, что я написАл (особенно кусок приведённого кода) ... т к я заведомо зделал ретрив пасса админу, подглядел валидатион и затих на какое то время ! незнаю с какой переодичностью он делал бэкапы, но свой старый пасс восстановил через админку на серваке ИМХО. Админ попался матёрый, поэтому он в панике не тыкал на кнопку ВСПОМНИТЬ пасс, это просто дело случая ...
    А колизию нашёл пасс про за пару минут, ну это дело случая т к можно найти быстрее и полным перебором ...
    Скрипту ретрива не важно что глотать, лиш бы при шифровке и обрезке получилось то что прописано в базе.
    Да и ещё одно, версия была не такая уж и древняя -) 1.0.13 (это немного упрощённая версия 1.1.4) в этой версии не хватает небольших и не столь значительных кусочков кода !


    LEE_ROY
    При бэкапе , даже если сменить пасс не через восстановление, а просто скажем через миадмин (т к админ думал что я просто подобрал его пасс) колонка валидатион не куда не девается и так же сохраняется в бэкап в обезательном! порядке.
     
  6. Dr.Frank

    Dr.Frank Elder - Старейшина

    Joined:
    31 Jul 2002
    Messages:
    301
    Likes Received:
    72
    Reputations:
    12
    способ действительно действенный, и причем перебор состоит из 10 цифр и 6-ти первых букв латинского алфавита, т.е. на все про все уйдет максимум 3 дня(у меня 2-х ядерный АМД 6400+). ну это конечно если без коллизии.. )))
     
  7. Elekt

    Elekt Banned

    Joined:
    5 Dec 2005
    Messages:
    944
    Likes Received:
    427
    Reputations:
    508
    2 n-000
    Вообще, баг интересный, молодец.

    Но я не вижу на официальном сайте никакой урезанной версии, помимо обновлений разной степени "облегчённости".
    Может оно и так, но пока не ясно где ты её взял.

    А пока - таже история с урезком наблюдается и в smf_2-0-beta3

    /Sources/Reminder.php

    PHP:
            $password substr(preg_replace('/\W/'''md5(rand())), 010);

            require_once(
    $sourcedir '/Subs-Post.php');
            
    $replacements = array(
                
    'REALNAME' => $row['real_name'],
                
    'REMINDLINK' => $scripturl '?action=reminder;sa=setpassword;u=' $row['id_member'] . ';code=' $password,
                
    'IP' => $user_info['ip'],
                
    'MEMBERNAME' => $row['member_name'],
                
    'OPENID' => $row['openid_uri'],
            );

            
    $emaildata loadEmailTemplate('forgot_' $context['account_type'], $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language $row['lngfile']);
            
    $context['description'] = $txt['reminder_' . (!empty($row['openid_uri']) ? 'openid_' '') . 'sent'];

            
    // If they were using OpenID simply email them their OpenID identity.
            
    if (!empty($row['openid_uri']))
                
    sendmail($row['email_address'], $emaildata['subject'], $emaildata['body']);
            else
            {
                
    // Set the password in the database.
                
    updateMemberData($row['id_member'], array('validation_code' => substr(md5($password), 010)));
            }
     
  8. n-000

    n-000 Elder - Старейшина

    Joined:
    25 Oct 2006
    Messages:
    90
    Likes Received:
    36
    Reputations:
    5
    El это не я её взял -) Этот форум уже был на серваке с которым я и проделывал эксперименты ...
    Я долго гуглил (хотел на локал хост такой же) и в гугле вычитал что это урезок, кстате обновления этого форума происходит через админку, а на офф сайте есть раздел на который нет ссылок, ну во всяком случае я не нашёл ... возникали мысли о платной версии но он бесплатен. Если интересует могу в ПМ скинуть ссыль на такого зверька.
     
  9. Elekt

    Elekt Banned

    Joined:
    5 Dec 2005
    Messages:
    944
    Likes Received:
    427
    Reputations:
    508
    Поговорил по поводу поддержки брута неполных хешей с автором пассвордпро - оказалось прога давно это держит.
     
  10. Dr.Frank

    Dr.Frank Elder - Старейшина

    Joined:
    31 Jul 2002
    Messages:
    301
    Likes Received:
    72
    Reputations:
    12
    RAND_MAX, как посмотрел на различных Linux и FreeBSD серверах, оказывается не более 9 999 999 999, т.е. не более 10 милиардов, итого 10 знаков.

    если запустить скрипт:
    PHP:
    <?php
    $time
    =time();
    for(
    $q=0;$q<10000000000;$q++){
    if((
    $q 1000000)==0){
    echo 
    $q." - ".date("H:i:s",time()-$time)."\r\n";
    }
    $t=substr(md5($q),0,10);
    if(
    substr(md5($t),0,10)=="72dc15205d"){
      echo 
    $t;
      break;
      }
    }
    ?>
    то, на моей системе, 1кк перебирается за 11 секунд. Чтобы перебрать 10ккк потребуется примерно 30-34 часов, (11*10000/3600).
    В то же время, если подбирать в PasswordsPro 10 символов из списка "0123456789abcdef", то для всего перебора потребуется 3 дня.
    Тем более скрипт можно расположить на различных серверах и запускать там(распределенный перебор).
     
    #10 Dr.Frank, 2 Feb 2009
    Last edited: 2 Feb 2009