Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
В этой статье я расскажу о двух уязвимостях в популярной CMS Drupal. Одна из них связана с архивами PHAR, не особенно страшна и работает только с правами админа, но если подключить другую (XSS), то такой дуэт становится уже очень опасным для любого сайта на незапатченном Drupal. Мы подробно разберем, как работают обе и откуда они взялись.
Итак, интересный случай XSS связан с тем, что разработчики не учли отдельные особенности работы некоторых функций PHP с кодировкой UTF-8. Из-за особенностей работы preg_replace
атакующий может загрузить файл, содержащий HTML/JS-код, при переходе на который он будет выполнен в контексте браузера пользователя.
Эта уязвимость получила номер CVE-2019-6341, ее обнаружил Сэм Томас (Sam Thomas). Под угрозой оказались все версии ветки Drupal 8.6 до 8.6.13, Drupal 8.5 до 8.5.14 и Drupal 7 до 7.65.
Вторая уязвимость — unserialize при помощи архива PHAR. Отсутствие проверки пути до временной директории дает возможность использовать враппер phar://
. В конце прошлого года я рассказывал об эксплуатации десериализации через архивы PHAR в форуме phpBB. В Drupal тоже есть возможность загрузки файлов и атакующий может загрузить картинку, содержащую полезную нагрузку. Затем указать путь до нее в качестве временной директории, используя поток phar
, что приведет к выполнению произвольного кода.
Чтобы воспроизвести уязвимость, нам понадобятся два контейнера Docker. Первый — для сервера базы данных.
$ docker run -d -e MYSQL_USER="drupal" -e MYSQL_PASSWORD="6zbd9Ilfka" -e MYSQL_DATABASE="drupal" --rm --name=mysql --hostname=mysql mysql/mysql-server:5.7
Второй — официальный, от разработчиков Drupal, с последней уязвимой к обоим багам версией — 8.6.5.
$ docker run -d --rm -p80:80 --link=mysql --name=drupalvh --hostname=drupalvh drupal:8.6.5
Теперь нужно пройти несложную процедуру инсталляции.
Установка Drupal 8.6.5
Если требуется отладка, то я по-прежнему рекомендую использовать PhpStorm и расширение Xdebug helper для браузера. Эта связка работает быстро и стабильно. Чтобы иметь возможность дебага, я дополнительно установлю PHP-расширение Xdebug.
$ docker exec -ti drupalvh /bin/bash
$ pecl install xdebug-2.6.1
$ echo "zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20170718/xdebug.so" > /usr/local/etc/php/conf.d/php-xdebug.ini
$ echo "xdebug.remote_enable=1" >> /usr/local/etc/php/conf.d/php-xdebug.ini
$ echo "xdebug.remote_host=192.168.99.1" >> /usr/local/etc/php/conf.d/php-xdebug.ini
$ service apache2 reload
Не забудь поменять IP-адрес 192.168.99.1 на свой и обрати внимание на путь до скомпилированной библиотеки xdebug.so
. Далее нужно скачать исходники Drupal, и после перезагрузки конфигов Apache можно запускать отладчик.
После завершения установки CMS необходимо будет создать тестовую страницу или запись.
Создание тестовой записи в Drupal
Помимо этого, для тестирования XSS понадобится любой пользователь, который сможет загружать файлы и оставлять комментарии. В дефолтной инсталляции это можно делать после прохождения регистрации. Как вариант, можешь просто создать юзера в админке.
Начнем с межсайтового скриптинга. У пользователей Drupal есть возможность комментировать записи. И, как и в любой современной CMS, в комментариях можно использовать базовую разметку. Функция, которая нам интересна, — это добавление картинок.
Добавление картинок в форме комментирования записи в Drupal
Причем картинка загружается с компьютера пользователя. Имена загружаемых файлов могут представлять опасность, одна из последних уязвимостей в WordPress тому пример.
За сохранение загруженных файлов отвечает функция _file_save_upload_single
.
core/modules/file/file.module
function _file_save_upload_single(SplFileInfo $file_info, $form_field_name, $validators = [], $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
...
$file->destination = file_destination($destination . $file->getFilename(), $replace);
Обрати внимание на аргумент $replace
. Он отвечает за ситуацию, когда в директории уже присутствует файл с таким же именем, как у загружаемого. По дефолту новый файл переименовывается.
core/includes/file.inc
function file_destination($destination, $replace) {
if (file_exists($destination)) {
switch ($replace) {
...
case FILE_EXISTS_RENAME:
$basename = drupal_basename($destination);
$directory = drupal_dirname($destination);
$destination = file_create_filename($basename, $directory);
...
return $destination;
Функция file_create_filename
генерирует новое имя для загружаемого файла. Но перед этим производится санитизация названия. Убираются все нежелательные символы.
core/includes/file.inc
function file_create_filename($basename, $directory) {
...
$basename = preg_replace('/[x00-x1F]/u', '_', $basename);
if (substr(PHP_OS, 0, 3) == 'WIN') {
// These characters are not allowed in Windows filenames
$basename = str_replace([':', '*', '?', '"', '<', '>', '|'], '_', $basename);
}
Функция preg_replace
заменяет на символ подчеркивания (_
) все символы с ASCII-кодом до 31 (1F). И все бы ничего, если бы не PCRE-модификатор u
(PCRE_UTF8). Он интерпретирует входные данные как строку UTF-8. Допустимая длина символа UTF-8 — от одного до четырех байт. UTF-8 спроектирован с учетом обратной совместимости с набором символов ASCII. Поэтому в диапазоне однобайтовых кодов (0x00—0x7F) ASCII и UTF-8 пересекаются.
При работе функций preg_*
с этой кодировкой есть небольшая особенность: если переданная строка имеет некорректный формат, то результатом работы будет NULL, а выполнение кода продолжится. Об этом четко написано в документации — см. справку по модификатору u
.
Источник: xakep.ru