Комбо для Drupal. Как захватить сайт с Drupal, используя новые уязвимости

В этой статье я расскажу о двух уязвимостях в популярной CMS Drupal. Одна из них связана с архивами PHAR, не особенно страшна и работает только с правами админа, но если подключить другую (XSS), то такой дуэт становится уже очень опасным для любого сайта на незапатченном Drupal. Мы подробно разберем, как работают обе и откуда они взялись.

Итак, интересный случай XSS связан с тем, что разработчики не учли отдельные особенности работы некоторых функций PHP с кодировкой UTF-8. Из-за особенностей работы preg_replace атакующий может загрузить файл, содержащий HTML/JS-код, при переходе на который он будет выполнен в контексте браузера пользователя.

INFO

Эта уязвимость получила номер 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, что приведет к выполнению произвольного кода.

INFO

Речь идет о CVE-2019-6339. Ей подвержены все версии Drupal 8.6 ниже 8.6.6, Drupal 8.5 ниже 8.5.9 и Drupal 7 ниже 7.62. Уязвимость была найдена Грегом Кнаддисоном (Greg Knaddison) из Drupal Security Team и Сэмом Томасом (Sam Thomas).

Стенд

Чтобы воспроизвести уязвимость, нам понадобятся два контейнера 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 понадобится любой пользователь, который сможет загружать файлы и оставлять комментарии. В дефолтной инсталляции это можно делать после прохождения регистрации. Как вариант, можешь просто создать юзера в админке.

Путь к 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.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


aLLy: Специалист по информационной безопасности в ONsec. Research, ethical hacking and Photoshop.