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

 

В чем смысл?

Существует сразу несколько сторонних реализаций PHP. Все они создавались с целью повышения производительности, а также расширения возможностей языка. При использовании сторонних реализаций PHP скорость работы приложений в среднем возрастает до пяти раз — показатель, несомненно, высокий. Достигается это благодаря использованию кросскомпиляции. В общем виде процесс компиляции осуществляется в два шага:

  1. PHP-сценарий транслируется в промежуточный код (как правило, это C-код);
  2. C-код компилируется в машинный.

Наиболее популярными и распространенными среди альтернативных реализаций PHP являются Roadsend PHP, Phalanger, Quercus on Resin, а также HipHop for PHP. Сначала я кратко расскажу о каждой из них, а потом приступим к самому интересному — проверим их на предмет безопасности.

 

Альтернативные реализации PHP

Roadsend PHP

Реализация Roadsend PHP состоит, по сути, из двух компонентов — компилятора и интегрированного веб-сервера, называемого MicroServer. Компиляция осуществляется с промежуточным транслированием PHP-кода в код на языке С. Встроенный веб-сервер позволяет запускать полученные в результате компиляции приложения без использования каких-либо дополнений и ухищрений. В случае если необходимо использовать привычный веб-сервер вроде Apache, lighttpd, nginx, то скомпилированное приложение придется связывать с веб-сервером посредством интерфейса CGI или FastCGI.

Phalanger

Phalanger представляет больший интерес для многих разработчиков, так как помимо лучшей, по сравнению с оригинальным PHP, производительностью, предоставляет и дополнительные возможности. Он позволяет обращаться PHP-приложениям почти ко всем .NET-конструкциям, благодаря чему можно создавать более гибкие веб-приложения, синтаксис которых не ограничивается одним PHP. Phalanger работает с веб-сервером IIS, и процесс их интеграции не сложнее, чем в случае с оригинальным PHP.

Quercus on Resin

Еще один пример стыка технологий — веб-сервер Resin. В первых версиях Resin представлял собой веб-сервер и сервер приложений для Java, но затем появилась реализация PHP, называемая Quercus. Resin имеет две ветки — Professional и Open Source. В версии Professional PHP-код компилируется в байт-код Java, в то время как в Open Source версии PHP-код интерпретируется.

HipHop for PHP

Последняя из рассмотренных реализаций — HipHop, разработчиком которой является компания Facebook. HipHop транслирует PHP-сценарии в промежуточный код на C++, а затем, используя компилятор g++, создает исполняемый файл. Следует отметить, что размер даже примитивного приложения, скомпилированного при помощи HipHop, достигает около 30 мегабайт. Но при этом приложение уже включает в себя веб-сервер: достаточно при запуске указать соответствующий ключ и порт, по которому приложение должно быть доступно. Помимо этого, как и в случае с Roadsend PHP, полученные приложения могут связываться с веб-серверами с помощью интерфейсов CGI или FastCGI. Компиляция занимает длительное время, и если в приложение постоянно вносятся изменения, то процесс обновления может стать серьезной головной болью. Однако есть и сильная сторона — исключаются некоторые категории уязвимостей:

  1. Подключение произвольных файлов (Local File Inclusion) — подключать можно только те файлы, которые присутствовали на момент компиляции;
  2. Загрузка произвольных файлов — загруженные файлы исполняться не будут, так как исполняемыми они не являются, а интерпретатор здесь не используется.
 

Подход к исследованию

Итак, перед нами стоит задача выявить проблемы безопасности сразу нескольких новых языков, синтаксис которых совпадает с PHP. Будет ли совпадать результат, особенности и поведение? А главное — насколько безопасно использование сторонних реализаций?

Сразу же возникает вопрос, как сравнить и на что нужно смотреть. Резонно выделить категории, по которым можно будет проверить каждую из реализаций:

  • уязвимости окружения — почти все реализации содержат собственные веб-серверы и прочее окружение, являющееся неотъемлемой частью веб-приложения;
  • механизмы обработки параметров, тут следует вспомнить атаки HTTP Parameter Pollution и HTTP Parameter Contamination, а также уделить внимание глобализации и типизации переменных;
  • уязвимости стыка технологий — новые возможности могут повлечь за собой и новые уязвимости;
  • уязвимости, которые встречались в очень старых версиях оригинального PHP.
 

Уязвимости окружения

Здесь отличился Roadsend PHP, а точнее, входящий в него веб-сервер MicroServer. Выяснилось, что он уязвим к очень простому варианту уязвимости Path Traversal. Пример эксплуатации приведен в листинге.

Эксплуатация Path Traversal в Roadsend PHP
http://host/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd

Как видно из запроса, тут все просто — используются переходы к родительскому каталогу, а к символу слеша при этом применяется URL-кодирование. В результате можно получить контент любого файла (см. рис. 1). Но в данном случае можно эксплуатировать проще — указывая абсолютный путь до файла:

Эксплуатация Path Traversal
http://host//etc/passwd

Проект Roadsend PHP с недавнего времени не поддерживается, хотя ресурсы, использующие его, еще остались. Владельцам этих ресурсов следует подобрать другое решение.

Рис.1. Контент файла /etc/passwd
Рис.1. Контент файла /etc/passwd
 

Обработка параметров

Как известно, различные платформы и приложения по-разному обрабатывают заведомо некорректные символы и конструкции: в каких-то случаях такие символы заменяются, а в каких-то случаях такая замена не осуществляется. На этом и основана атака HTTP Parameter Contamination. Обычно она используется с целью обхода различных фильтраций, а также для формирования специфических векторов client-side атак. В табл. 1 приведены расхождения в обработке некорректных символов для различных реализаций PHP — за эталон принят обычный LAMP. Как видно, результат отличается от оригинального PHP. Причем расхождения получились почти идентичными для Phalanger и Quercus. И помимо возможности создавать переменные, именем которых является пустая строка, в обоих случаях можно добиться вывода ошибки 500 (см. забавный fingerprint на рис. 2).

Рис.2. Пример 500 ошибки в Quercus
Рис.2. Пример 500 ошибки в Quercus

Возможность создавать переменные, содержащие в имени символ пробела и тем более null-byte, скажется в некорректной работе приложения при циклических обходах массивов (см. листинг), когда используется не только значение переменной, но и ее имя.

Циклический обход массива, уязвимость Local File Inclusion

foreach($_GET["language"] as $langDir => $langFile) {
   include($langDir."/".$langFile.".php");
}

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

Подключение файла /etc/passwd
http://host/index.php?language["/etc/passwd%00"]=1
 

Глобализация и перезапись переменных

Возможность задавать значения переменных напрямую — брешь в безопасности веб-приложений. В оригинальном PHP за подобное поведение отвечает опция register_globals, причем начиная с версии 5.4.0 она удалена. Логично предположить, что в сторонних реализациях PHP не все так гладко, как хотелось бы. В Quercus опция register_globals отсутствует (сами разработчики называют ее «черной дырой в безопасности»), однако при передаче параметров методом POST происходит их глобализация, а при заявленном отсутствии опции этого быть не должно. Но это не главная проблема — гораздо опаснее то, что параметры, переданные методом POST, могут перезаписывать элементы массива _SERVER. На рис. 3 приведен пример перезаписи значения элемента_SERVER["REMOTE_ADDR"], что, по сути, приводит к подмене значения клиентского IP-адреса.

Рис.3. Перезапись элемента массива _SERVER
Рис.3. Перезапись элемента массива _SERVER

Наиболее опасный вектор атаки — подмена значения элемента $_SERVER["DOCUMENT_ROOT"], содержащего абсолютный путь до веб-каталога, и развитие атаки Local File Inclusion (см. рис. 4). Следует отметить, что даже безопасный для оригинального PHP код (см. листинг) становится уязвимым, если существует возможность перезаписи переменных.

Использование $_SERVER["DOCUMENT_ROOT"] в коде

<?php
include($_SERVER["DOCUMENT_ROOT"]."header.php");
?>

Рис.4. Перезапись $_SERVER["DOCUMENT_ROOT"]
Рис.4. Перезапись $_SERVER[«DOCUMENT_ROOT»]
Описание уязвимости приведено в advisory: bit.ly/MFeJYu. Исправление уязвимости ожидается в новых версиях. Интересно то, что перезаписать элементы массива _SESSION не получится: попытка перезаписи заканчивается сообщением об ошибке. Оно и к лучшему, так как иначе любой механизм авторизации, использующий массив _SESSION, становился бы уязвимым.

 

Типизация переменных

В PHP существует так называемое гибкое сравнение, позволяющее сравнивать переменные различного типа (при тождественном сравнении переменных различного типа его результат всегда будет false). Сравнение происходит с некоторыми особенностями, они сведены в таблицу на официальном сайте PHP. Несоблюдение данных особенностей может привести к непредсказуемой работе приложения. Расхождения с оригинальным PHP нашлись достаточно быстро. В сценариях, приведенных в листинге, осуществляется сравнение пустого массива с переменными различного типа.

Гибкое сравнение переменных различного типа

// script 1
<?php
$xArray = array(TRUE, FALSE, 1, 0, -1, "1", "0", "-1", NULL, array(), "php", "");
foreach($xArray as $x) {
    if($x == array()) { echo("TRUE"); } else { echo("FALSE"); }
    echo("<br>");
}
?>

// script 2
<?php
$xArray = array(TRUE, FALSE, 1, 0, -1, "1", "0", "-1", NULL, array(), "php", "");
foreach($xArray as $x) {
    if(array() == $x) { echo("TRUE"); } else { echo("FALSE"); }
    echo("<br>");
}
?>

Различаются сценарии порядком следования сравниваемых переменных, но, по сути, они идентичны. Соответственно, результаты сравнения также должны совпадать. Но, как видно из табл. 3, в Quercus результат сравнения зависит от порядка следования сравниваемых переменных, что является нетипичным поведением для PHP. Кроме этого, результат сравнения пустого массива array() и 0 является истиной, что также не типично для оригинального PHP. Это может привести к обходу различных проверок, например в механизмах аутентификации или авторизации.

 

Уязвимости стыка технологий

Как известно, в PHP существует возможность устанавливать различные ограничения безопасности. Например, можно использовать опцию disable_functions, запрещающую вызывать указанные функции (как правило, это функции, выполняющие shell-команды), или опцию open_basedir, ограничивающую доступ к файловой системе. Но обычно возможность использования сторонних конструкций не учитывается.

Использование опции disable_functions для запрета выполнения shell-команд

disable_functions: system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec

Таким образом, используя для выполнения shell-команд конструкции .NET, можно обходить заданное ограничение безопасности. Пример обхода приведен в листинге.

Выполнение shell-команд через конструкции .NET

<?php
$process = new Diagnostics\Process();
$process->StartInfo->FileName = "cmd.exe";
$process->StartInfo->WorkingDirectory = "C:\\";
$process->StartInfo->Arguments = "/c ".$_GET["cmd"];
$process->Start();
$process->WaitForExit(); 
?>
 

Old School

— Межсайтовое выполнение сценариев в сообщениях об ошибках

Отличились Roadsend PHP и Quercus — в сообщениях об ошибках служебные символы не заменяются на их HTML-эквиваленты, в результате чего возможно проведение атак на пользователей сайта (см. рис. 5). Видимо, разработчики забыли, что на дворе 2012 год, а не 2002-й?

Рис.5. Межсайтовое выполнение сценариев
Рис.5. Межсайтовое выполнение сценариев

— Path Traversal в имени загружаемого файла

Quercus, входящий в состав 3-й ветки веб-сервера Resin, уязвим к Path Traversal в механизме загрузки файлов на сервер. Переходы к родительскому каталогу не удаляются из имени файла. В результате можно загружать файлы в произвольный каталог — пример запроса приведен в листинге.

Таб. 1. Различия в обработке некорректных символов
Таб. 1. Различия в обработке некорректных символов
Таб. 2. Различия в обработке некорректных символов
Таб. 2. Различия в обработке некорректных символов
Таб. 3. Зависимость результата сравнения от порядка следования сравниваемых переменных
Таб. 3. Зависимость результата сравнения от порядка следования сравниваемых переменных

Пример HTTP-запроса, загружающего файл в родительский каталог

POST http://127.0.0.1:8080/test/file.php HTTP/1.1
…
Content-Type: multipart/form-data; 
boundary=---------------------------101412320927450
Content-Length: 228

-----------------------------101412320927450
Content-Disposition: form-data; name="test"; 
filename="../shell.php"
Content-Type: application/octet-stream

<?php
phpinfo();
?>
-----------------------------101412320927450--

Описание уязвимости приведено в advisory: bit.ly/MFeJYu. Исправление уязвимости ожидается в новых версиях.

— Null-byte в имени загружаемого файла

Path Traversal не единственная проблема при загрузке файлов в Quercus. Еще одна не менее опасная проблема — возможность передачи в имени файла null-byte, что позволяет отбросить принудительно добавляемый к имени загружаемого файла постфикс (например, расширение jpg), а также обходить ряд проверок безопасности. Пример проверки, которую можно обойти, приведен в следующем листинге.

Пример проверки расширения файла

<?php
 if(isset($_FILES["image"])) {
  if(!preg_match('#\.(jpg|png|gif)$#', $_FILES["image"]["name"])) {
die("Hacking attempt!");}

  copy($_FILES["image"]["tmp_name"],
"./uploads/".$_FILES["image"]["name"]
 );
}
?>

Рис.6 Null-byte в имени загружаемого файла
Рис.6 Null-byte в имени загружаемого файла

В приведенном сценарии осуществляется проверка расширения файла: оно должно быть одним из допустимых (jpg, png или gif), и если это не так, то файл загружен не будет. Сама по себе проверка более чем адекватная, но при возможности использования null-byte в имени файла обойти такую проверку труда не составит — достаточно передать в имени файла null-byte, а затем .jpg. Таким образом проверка будет пройдена, а в момент копирования файла строка .jpg будет отброшена. Исправление уязвимости ожидается в новых версиях.

 

Positive Hack Days 2012

Данная статья основана на выступлении Сергея Щербеля на международном форуме по практической безопасности Positive Hack Days 2012. Напомню, PHDays — это международный форум, посвященный вопросам практической информационной безопасности. Своим появлением PHDays поставил точку в разговорах хакерской тусовки, посвященных идеям на тему «Как было бы круто иметь свой DEF CON или Black Hat в России». Мы получили оба в одной бутылке :). PHDays — это место, где футболки встречаются с пиджаками, а парни с Античата обсуждают результаты взлома интернет-банка с топ-менеджером из финансовых структур. Презентация, по мотивам который подготовлен этот материал, доступна по адресу slidesha.re/Nl2VLF. Получить информацию о самом мероприятии, а также посмотреть список доступных материалов ты можешь на официальном сайте этой уникальной хакерской конференции (www.phdays.ru).

 

Подводим итоги

Все рассмотренные реализации PHP имеют значительное преимущество в производительности (прирост до пяти раз, что очень недурно), но при этом почти у всех есть проблемы безопасности:

  • уязвимое окружение;
  • проблемы с обработкой параметров (глобализация и типизация);
  • различные нарушения логики;
  • Path Traversal в различных проявлениях и другие.

Из-за указанных уязвимостей даже безопасное веб-приложение становится уязвимым при использовании сторонних реализаций PHP. Яркий пример — реализация Quercus, которая оказалась самой уязвимой из рассмотренных. Хотя есть и исключение: HipHop в чем-то даже безопаснее оригинального PHP.

 

 

Оставить мнение

Check Also

Твой тайный туннель. Детальный гайд по настройке OpenVPN и stunnel для создания защищенного канала

У тебя могут быть самые разные мотивы, чтобы пользоваться VPN: недоверенные сети, разного …