Ошибки десериализации классов на живых примерахПриветствую тебя, читатель! В январском номере ][ мы подробно рассмотрели один из новейших багов в PHP от известного специалиста по информационной безопасности Стефана Эссера. Как ты наверное помнишь, баг заключается в небезопасном использовании функции unserialize применительно к объектам. В прошлой статье я говорил о том, что данное исследование направлено на будущее, так как в реальных веб-приложениях более или менее серьезные уязвимости десериализации пока не были обнаружены. И вот… Будущее уже наступило! Пришла пора показать тебе подробный разбор таких багов в популярнейших скриптах Piwik и phpMyAdmin. Немного о PiwikДля начала расскажу тебе немного о Piwik. Итак, Piwik – это бесплатный скрипт веб-аналитики, позиционируемый как опенсорсная замена Google analytics. Эта система выросла из менее навороченного скрипта phpMyVisites. Функционал Пивика впечатляет: продвинутая система плагинов (похожая на аналогичную в WordPress), удобный API (ты можешь получать любую инфу из базы данных в форматах xml, json, php, csv), интерфейс юзера, основанный на виджетах (с drag and drop-примочками), перевод на множество языков, real time-репорты и многие другие фичи уже сделали этот скрипт мегапопулярным среди веб-мастеров (только последнюю версию скачали около 250 тысяч раз). Популярность скрипта подтверждают те факты, что Piwik несколько раз становился проектом месяца на sourceforge.net и выигрывал премию "Infoworld Bossie Award" как лучшее опенсорсное программное обеспечение. Я могу еще долго описывать все преимущества скрипта, но настало время рассказать о природе unserialize бага в Piwik. Zend FrameworkКак ты уже знаешь, Стефан Эссер в своей презентации приводил теоретические примеры выполнения произвольного PHP-кода в Zend Framework и писал о том, что сам фреймворк не подвержен уязвимости – уязвимы лишь те приложения, которые его используют вкупе с недостаточной проверкой данных в функции unserialize(). Как оказалось, Piwik как раз-таки и является тем самым приложением :) Теперь давай проследим за реверсингом скрипта, который провел сам Эссер (качай по ссылке в сносках последнюю уязвимую версию 0.4.5 из архива релизов). Открывай файл ./core/Cookie.php и находи следующую функцию:
Как видно, здесь Пивик получает контент из куков, которые передает пользователь, и разбирает их на запчасти:
Эта функция юзается практически в любом месте скрипта (например, для авторизации по кукам) и доступна любому удаленному пользователю, так что нам осталось только найти путь к опасным функциям в Zend Framework. Двигаем дальше :) РеверсингЕсли ты читал оригинальный PDF Эссера, то должен помнить, что наиболее удобным классом для выполнения произвольного кода во фреймворке является Zend_Log. Так что ищем и находим этот класс в ./libs/Zend/Log.php и смотрим на его деструктор:
Здесь деструктор в цикле выполняет некий метод shutdown() из классов, перечисленных в массиве _writers. Далее нужно найти полезный нам shutdown-метод. И таковой находится в ./libs/Zend/Log/Writer/Mail.php:
Этот шатдаун-метод проверяет, есть ли некие события, которые еще не были отправлены по указанному в свойстве адресу e-mail. Если такие находятся, то он отправляет их. Эта фича позволяет любому взломщику рассылать спам через тот же самый unserialize-баг. Спам-баг, конечно, может быть интересен определенному кругу читателей, но мы со Стефаном не останавливаемся на достигнутом и идем дальше :). Теперь нам необходимо найти классы, использующие метод render. Наиболее полезный из таковых оказывается в классе Piwik_View из файла ./core/View.php:
Как пишет сам Эссер, этот метод делает кучу интересных вещей, которые могут быть проигнорированы, и в конце вызывает известный шаблонный движок Smarty для рендеринга темплейта. Опасный SmartyИзвестно, что Smarty может выполнять PHP-код в темплейтах, поэтому мы и выбрали для дальнейшего исследования этот класс. Итак, смотрим на указанную выше функцию fetch() в ./libs/Smarty/Smarty.class.php:
Здесь имя шаблона подставляется в метод _compile_resource для компиляции:
Перед компиляцией методом _fetch_resource_info получается расширенная информация о ресурсе:
Бинго! С помощью стандартной PHP-функции call_user_func_array мы сможем выполнить любую другую callback-функцию :). Выполнение кодаВ данном примере функция call_user_func_array вызывается с двумя параметрами: имя вызываемой callback-функции и массив с тремя параметрами, которые передадутся в коллбэк. В контексте выполнения произвольного PHP-кода здесь сразу встают две проблемы:
Из-за этих ограничений Стефану пришлось придумать небольшой трюк, который заключается в использовании встроенного в Smarty враппера для eval:
Хотя эта функция по определению принимает лишь 2 параметра, ей возможно передать и большее их количество. Причиной этого является тот факт, что по дефолту пользовательские функции в PHP, в отличие от внутренних, могут оперировать произвольным числом параметров. Теперь нам осталось лишь собрать в единый эксплойт все результаты реверсинга, что Стефан Эссер уже сделал (ссылку ищи в сносках). В сплойте сериализуются все перечисленные выше классы и необходимые им методы, затем полученное значение пропускается через base64_encode и, на основе его, формируется evil-кукис, который ты сможешь скормить своему браузеру и наслаждаться результатами выполнения произвольного PHP-кода в Piwik. Также советую обратить внимание на еще один эксплойт по ссылке в сносках, где Эссер проворачивает еще один трюк с unserialize в Пивике и записывает произвольные файлы в произвольное место системы. Потрошим phpMyAdminТеперь небольшой бонус от меня. Если ты следишь за лентами эксплойтов, то, наверняка, не должен был пропустить уязвимость популярнейшего менеджера баз данных MySql phpMyAdmin версий до 2.11.9. Уязвимость заключалась в том, что скрипт установки ./scripts/setup.php вообще не проверял пользовательские данные, которые затем записывались в конфигурационный файл. Эксплойт был всем хорош, за исключением того, что администратор уязвимого хоста должен был вручную создать директорию ./config и дать ей права на запись (именно туда должен был записываться ядовитый конфиг), что на практике встречалось крайне редко. Настало время исправить это недоразумение. Итак, ./scripts/setup.php – единственное место в скрипте, где используется наша любимая функция unserialize:
Как можно видеть, параметр $_POST['configuration'] перед вставкой в unserialize() никоим образом не проверяется, так что мы вполне можем поискать интересные реализации волшебных методов __wakeup и __destruct. Очень полезный нам вэйкап-метод находится в ./libraries/Config.class.php:
В данном методе происходит много чего интересного, но сейчас нас интересует функция load(). Находим ее в том же файле:
Надеюсь, что ты увидел здесь eval-конструкцию, которая позволит нам выполнить любой PHP-код и в которую мы сможем вставить все что угодно :). Сделать это позволит простенький метод getSource и обход ограничений безопасности в методе checkConfigSource:
Последний метод выполняет всяческие проверки с тем файлом, который, по идее,
должен быть конфигурационным. Функции file_exists(), is_readable() и fileperms()
изначально были призваны помешать вставить в нужный нам file_get_contents()
удаленный URL с PHP кодом на выполнение. Исходя из всего написанного выше, уже можно составить ядовитое значение, которое будет передаваться в unserialize, для уязвимого параметра $_POST['configuration'] (устанавливаем только свойство "source"):
Чтобы вывести на экран phpinfo(), файл shell.txt на твоем ftp-хосте должен иметь значение "<? phpinfo();exit; ?>" (exit; в конце обязательно, иначе скрипт вылетит с "Fatal error"). Удобный эксплойт для этой баги также ищи в сносках. Epic WinУязвимости, основанные не на невнимательности программистов, а на особенностях самого PHP, о которых не все знают, набирают силу! С каждым днем их появляется все больше и больше. Этот факт заставляет нас гораздо глубже анализировать свой (и чужой :) код и учиться лучше понимать саму природу информационной безопасности. А я надеюсь, что ты, изучив вместе со мной по шагам всю подноготную unserialize-бага от Стефана Эссера, сам захочешь принять участие в пентестинге известных движков. INFOСоветую прочитать отличную заметку на тему нашей статьи в блоге Raz0r'а. Тут ты также найдешь неплохой анализ unserialize-бага в нелицензионных версиях форума vBulletin, который, к сожалению, позволяет выполнить лишь функции без параметров. Ссылкиpiwik.org – официальный сайт Piwik builds.piwik.org/?C=M;O=D – архив релизов Piwik suspekt.org/2009/12/09/advisory-032009-piwik-cookie-unserialize-vulnerability – Piwik Cookie unserialize() Vulnerability framework.zend.com/download – официальная страница Zend Framework smarty.net – официальный сайт Smarty php.net/call_user_func_array – описание функции call_user_func_array() suspekt.org/downloads/Piwik_Smarty.txt – выполнение произвольного кода в Piwik через Smarty suspekt.org/downloads/Piwik_Config.txt – запись произвольных файлов в Piwik gnucitizen.org/static/blog/2009/06/phpmyadminrcesh.txt – phpMyAdmin '/scripts/setup.php' PHP Code Injection RCE PoC v0.11 snipper.ru/view/12/phpmyadmin-2119-unserialize-arbitrary-php-code-execution-exploit – мой эксплойт для phpMyAdmin <= 2.11.9 |