Как эксплуатировать уязвимость PHP OBJECT INJECTION

Уязвимости связанная с преобразованием непроверенных сериализованных данных в представление PHP, они же unserialize-баг, все еще актуальны. Более того, проведенное мной исследование встроенных классов PHP показало, что возможны и новые, более универсальные
способы эксплуатации, использующие уязвимости самого интерпретатора.

PHP OBJECT INJECTION

В 2009 году небезызвестный Стефан Эссер на конференции Power of Community в Сеуле выступил с исследованием Shocking News in PHP Exploitation, где, помимо прочего, выявил возможные сценарии атак, когда пользовательские данные попадают в функцию unserialize(). Можно сказать,
что именно тогда появился новый термин PHP Object Injection, обозначающий уязвимость, позволяющую внедрять произвольные объекты PHP в контекст веб-приложения. Уязвимость позволяет проводить множество атак: от XSS до выполнения произвольного кода, в зависимости от структуры объектов уязвимого приложения.

Сериализованные данные после преобразования в объект PHP могут изменить рабочий процесс приложения с помощью так называемых магических методов. Например, если у класса определен магический метод __get(), то он будет вызываться каждый раз, когда у объекта запрашивается несуществующее свойство. При эксплуатации PHP Object Injection наиболее полезны методы __wakeup() и __destruct(): первый вызывается при десериализации объекта, второй — при выгрузке объекта, то есть при завершении работы скрипта. Оба метода вызываются автоматически, без необходимости проводить какие-либо операции над объектом.

В сложных современных веб-приложениях без объектноориентированного подхода просто не обойтись, поэтому очень часто можно встретить деструкторы классов, которые выполняют действия для выгрузки объекта. Например, класс взаимодействия с базой данных закрывает соединение, класс кеша может удалять временные файлы. Именно в деструкторах таится главная опасность для разработчиков, которые зачастую не учитывают, что при десериализации возможна подмена свойств объекта на произвольные значения. Многие популярные веб-приложения были затронуты
PHP Object Injection, в их числе Invision Power Board, Joomla и vBulletin.

В последней версии vBulletin я обнаружил любопытный unserialize(). Он показал, что полезными могут оказаться не только __wakeup() и __destruct(), все зависит от того, какие данные ожидает веб-приложение после десериализации данных. И более того, необязательно ориентироваться только на собственные классы приложения, ведь есть и внутренние классы PHP, речь о которых пойдет ниже.

VBULLETIN

В плане безопасности популярный форум vBulletin знал лучшие годы. В пятой версии «вобла» еще дальше ушла от простого форума и стала громоздким комьюнити-комбайном. Ради праздного интереса я открыл папку core (которая, к слову, не защищена .htaccess), где в инклудах обнаружил исходники Apache log4php. Данный фреймворк — удобный инструмент логирования в PHP и используется в таких проектах, как SugarCRM, vtiger и CMS Made Simple. Но если в этих веб-приложениях от log4php остался только основной функционал, то в vBulletin обнаружился полный код фреймворка, в том числе доступная из веба папка с примерами использования логфреймворка. Один из скриптов оказался крайне любопытным: при обращении к нему на порту
4242 запускался сервер, который при получении данных пытался их десериализовать. Так как полезных магических методов в log4php найти не удалось, было решено обратиться к классам PHP. С помощью следующего скрипта я получил список всех магических методов в объявленных классах:

<?php
$classes = get_declared_classes();
foreach($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array('__destruct', '__toString', '__wakeup', '__call', '__callStatic', '__get', '__set', '__isset', '__unset', '__invoke', '__set_state'))) {
print $class . '::' . $method . "n";
}
}
}

Список получился объемным, но, как оказалось, почти все классы не позволяли сериализацию либо были бесполезными. Внимание привлек класс SoapClient и его метод __call(), который вызывается, если попытаться вызвать несуществующий метод. Это как раз то, что нужно, ведь в уязвимом скрипте после преобразования данных в объект вызывался метод getRootLogger(). Класс SoapClient имеет множество свойств, а при вызове несуществующего метода он позволяет отправлять SOAP-запросы на произвольные адреса. Уже интересно, посмотрим, что можно из этого выжать.

XXE через unserialize в Joomla
XXE через unserialize в Joomla

SOAPCLIENT

Конструктор класса SoapClient принимает два аргумента: адрес WSDL-документа в формате XML, описывающий SOAP интерфейс, а также массив опций. Если передать в качестве $wsdl значение NULL, то объект будет инициализирован в non-WSDL режиме. Проблема в том, что при режиме с WSDL-документом класс не предусматривает нормальной сериализации свойств, а вот с non-WSDL все в порядке. Поэтому остается один вариант с $wsdl=null и ограниченным набором опций. Но даже с тем, что было доступно, получилось довольно много. Начнем с банальной XSS’ки, конструируем следующий объект SoapClient:

<?php
$c = new SoapClient(null, array('uri'=>'http:// test.com/', 'location'=>
'http://test.com/api.php'));

Контролируемый нами скрипт api.php отдает HTTP-ответ
с кодом 404:

<?php
header("HTTP/1.0 404 <script>alert(1)</script>");

Вместо статус-сообщения «Not Found» api.php отправляет произвольную строку, SoapClient видит код 404, генерирует исключение SoapFault, сообщая о том, что удаленный ресурс не найден, и возвращает нашу строку в тело ответа.

Работа класса SoapClient
Работа класса SoapClient

Одна из возможностей SoapClient — локальное кеширование WSDL-документа. Хотя это не помогло бы эксплуатации PHP Object Injection в vBulletin, все же стало интересно, как это делает SoapClient. Оказалось, что документ сохраняется без всяких проверок соответствия пути директиве конфигурации PHP open_basedir, которая запрещает файловые операции вне указанного каталога:

<?php
ini_set('open_basedir', '/var/www');
ini_set('soap.wsdl_cache_enabled', true);
ini_set('soap.wsdl_cache_dir', '/tmp');
$c = new SoapClient('http://test.com/test.wsdl',
array('cache_wsdl' => WSDL_CACHE_DISK));

Итак, нужно было что-то более интересное, чем просто XSS. Почему бы не XXE, ведь SOAP работает с XML? И действительно, SoapClient был уязвим перед внедрением внешних XML-сущностей, даже несмотря на то, что при попытке парсинга DOCTYPE сообщал, что он не поддерживается! Дело в том, что исключение вызывалось уже после обработки DTD, а в используемом XML-парсере LibXML опция обработки внешних сущностей включена по умолчанию. Никакого вывода сущностей добиться не удалось, однако помогло замечательное исследование реализации XXE через внешние каналы связи моих коллег Алексея Осипова и Тимура Юнусова. Техника позволяет отправлять содержимое файла через HTTP-запросы прямо в запрашиваемом пути. А вместе с разнообразными PHP-обработчиками схем удалось добиться чтения любых файлов.
В этом помог враппер php:// и фильтр base64:

php://fi lter/read=convert.base64-encode/resource=/etc/passwd

Итак, эксплуатация PHP Object Injection через внутренний класс PHP оказалась успешной. Помимо vBulletin, данный вектор будет работать в Joomla <=3.0.3, где через unserialize() возможна SQL-инъекция и удаление произвольной директории.

Схема реализации XSS через SoapClient
Схема реализации XSS через SoapClient

Вполне очевидно, что не всегда можно встретить вызов метода на десериализованном объекте, и, к сожалению, в классах PHP нет ни одного деструктора, который брал бы из свойства объект и пытался выполнить у него какой-либо метод. Если обратиться к популярным компонентам, то одним из самых подходящих будет шаблонизатор Smarty. В нем можно встретить следующий код:

<?php
public function __destruct()
{
if ($this->smarty->cache_locking &&isset($this->cached) && $this->cached->is_locked) {
$this->cached->handler->releaseLock($this->smarty, $this->cached);
}
}

Если поместить в свойство handler наш объект SoapClient, то операции над объектом в коде веб-приложения не потребуются и XXE будет проэксплуатирована автоматически через метод __destruct().

WHAT’S NEXT?

В PHP 5.5 намечается нововведение в функции unserialize(), а именно второй аргумент, который позволит разработчикам запретить обработку объектов либо указать белый список разрешенных. В настоящее время используются регулярные выражения, которые зачастую можно легко обойти, либо данные не проверяются вовсе. Например, в Invision Power Board <= 3.3.4 была вот такая смешная проверка:

$_value = $_COOKIE[ ipsRegistry::$settings['cookie_id'].$name ];
if ( substr( $_value, 0, 2 ) == 'a:' ) {
return unserialize( stripslashes( urldecode( $_value ) ) );
}

В строке проверяются первые два символа: если это «a:», то есть сериализованный массив, то строка попадает в unserialize(). Однако атакующему ничто не мешает отправить массив объектов, что приведет к обходу проверки.

Говоря о будущем PHP Object Injection, стоит рассказать о PHP-фреймворке следующего поколения под именем Phalcon, который становится все более популярным у вебразработчиков. Весь его код написан на чистом C, что делает его самым быстрым PHP-фреймворком. Phalcon реализован в качестве расширения PHP, соответственно, требует компиляции и включения в конфигурацию PHP. При этом все классы фреймворка становятся доступными в контексте веб-приложения без каких-либо инклудов внешних файлов. Если представить shared-хостинг, у которого Phalcon включен для всех пользователей по умолчанию, то очевидно, что это позволит использовать классы фреймворка через unserialize(), даже если уязвимое веб-приложение написано не на его базе. Я решил проверить, возможна ли эксплуатация unserialize() через классы Phalcon, и оказалось — очень даже просто!

PHALCON

Получив магические методы из всех классов фреймворка, я не нашел ни одного деструктора, но обнаружил один__wakeup():

<?php
PHP_METHOD(Phalcon_Logger_Adapter_File, __wakeup){
zval *path, *options, *mode = NULL, *fi le_handler;
PHALCON_MM_GROW();
PHALCON_OBS_VAR(path);
phalcon_read_property_this(&path, this_ptr, SL("_path"), PH_NOISY_CC);
PHALCON_OBS_VAR(options);
phalcon_read_property_this(&options,this_ptr, SL("_options"), PH_NOISY_CC);
if (phalcon_array_isset_string(options,SS("mode"))) {
PHALCON_OBS_VAR(mode);
phalcon_array_fetch_string(&mode, options,SL("mode"), PH_NOISY_CC);
} else {
PHALCON_INIT_NVAR(mode);
ZVAL_STRING(mode, "ab", 1);
}
// Re-open the fi le handler if the logger was
// serialized
PHALCON_INIT_VAR(fi le_handler);
PHALCON_CALL_FUNC_PARAMS_2(fi le_handler,"fopen", path, mode);
phalcon_update_property_this(this_ptr,SL("_fi leHandler"), fi le_handler TSRMLS_CC);
PHALCON_MM_RESTORE();
}

Данный код получает из protected-свойства _path путь и открывает его функцией fopen() в режиме, передающемся в свойстве _options. С помощью данного кода можно открыть тысячи файлов и забить все дескрипторы, но это неинтересно. Но что, если поместить в _path объект? При вызове fopen() он будет преобразован в строку, за что отвечает магический метод __toString(). А вот этот метод Phalcon использует очень широко. Я не буду описывать все внутренности фреймворка, скажу
лишь, что в одном из __toString() удалось добиться инициализации произвольных объектов и вызова любых методов. Уже на этом этапе можно подсунуть наш SoapClient и провести XXE, но RCE всегда лучше! Пробежавшись по исходникам на предмет вызова функции phalcon_require  (инклудит файлы так же, как и require в PHP), я обнаружил класс PhalconMvcViewEnginePhp, в котором метод render позволяет инклудить произвольные файлы. Таким образом, через __wakeup() удалось вызвать __toString, а через него — подключение произвольных файлов.

RCE с помощью классов фреймворка Phalcon
RCE с помощью классов фреймворка Phalcon

PHP Object Injection все еще жив и будет жить. Вместе с «безопасной» версией unserialize() в PHP 5.5 появится много новыхклассов, и, возможно, не без уязвимостей. Так что смотрим в будущее!

Ссылки:

PHP Object Injection на OWASP: bit.ly/15vbvAW
Shocking News in PHP Exploitation: bit.ly/11mcvHy
Магические методы PHP: bit.ly/172anb
Apache log4php: bit.ly/aF7jHG
XXE Out-of-Band Data Retrieval: bit.ly/12pyfWb

Click to rate this post!
[Total: 3 Average: 5]

Специалист в области кибер-безопасности. Работал в ведущих компаниях занимающихся защитой и аналитикой компьютерных угроз. Цель данного блога - простым языком рассказать о сложных моментах защиты IT инфраструктур и сетей.

1 comments On Как эксплуатировать уязвимость PHP OBJECT INJECTION

Leave a reply:

Your email address will not be published.