Недавно мир узнал о серьезной уязвимости в системе управления контентом Drupal — мы тогда подробно писали об этом. Однако, разобрав проблему в ветке 8.x, мы оставили за кадром аналогичную брешь в Drupal 7. А ведь на этой версии сейчас работает куда больше сайтов! Эксплуатировать уязвимость в ней сложнее, но не намного. Сейчас я покажу, как это делается.
 

Подготовка

Для демонстрации уязвимости проще всего использовать официальный репозиторий Drupal на Docker Hub. Разворачиваем контейнер с нужной версией CMS. Сначала поднимаем сервер БД.

$ docker run -d -e MYSQL_USER="drupal" -e MYSQL_PASSWORD="7C4TYVARsy" -e MYSQL_DATABASE="drupal" --rm --name=mysql --hostname=mysql mysql/mysql-server

Теперь дело за контейнером с CMS. В этот раз берем самую старую уязвимую версию — 7.57.

$ docker run -d --rm -p80:80 -p9000:9000 --link=mysql --name=drupalvh --hostname=drupalvh drupal:7.57

Теперь через веб-интерфейс устанавливаем Drupal и проверяем, все ли у нас работает.

Установка Drupal 7.57
Установка Drupal 7.57

Еще неплохо было бы завести отладчик. Для этого я дополнительно установлю расширение Xdebug.

$ pecl install xdebug
$ echo "zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20151012/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. После перезагрузки конфигов Apache можешь запускать свой любимый дебаггер. В работе я по-прежнему использую PhpStorm и расширение Xdebug helper для Chrome.

Теперь скачиваем исходники CMS, слушаем 9000-й порт — и вперед, к победам.

 

Первые шаги

Перейдем на страницу создания нового аккаунта. В седьмой версии она значительно аскетичнее, чем в восьмой.

Создание нового аккаунта в Drupal 7
Создание нового аккаунта в Drupal 7
Создание нового аккаунта в Drupal 8
Создание нового аккаунта в Drupal 8

Из-за того, что отсутствует возможность загрузить аватар, стандартный вектор эксплуатации тут не сработает. Значит, нужно найти новый! Суть бага все та же — это внедрение элементов в Renderable Arrays, которые будут обработаны с помощью Render API. Существуют специальные элементы, которые вызывают функцию call_user_func с кастомными параметрами.

Для начала посмотрим, как обрабатываются роуты в приложении. Если у тебя на сервере включены семантические URL, то URI перенаправляются на файл index.php как GET-параметр q.

/index.php
19: require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
20: drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
21: menu_execute_active_handler();
/includes/menu.inc
510: function menu_execute_active_handler($path = NULL, $deliver = TRUE) {
...
521:   if ($page_callback_result == MENU_SITE_ONLINE) {
522:     if ($router_item = menu_get_item($path)) {

Например, для отображения страницы http://drupal.vh/user/register будет выполнен запрос http://drupal.vh/index.php?q=user/register.

Обработка роутов в Drupal 7
Обработка роутов в Drupal 7
/includes/menu.inc
455: function menu_get_item($path = NULL, $router_item = NULL) {
456:   $router_items = &drupal_static(__FUNCTION__);
457:   if (!isset($path)) {
458:     $path = $_GET['q'];
459:   }
460:   if (isset($router_item)) {
461:     $router_items[$path] = $router_item;
462:   }

Далее путь разбивается при помощи функции array_slice и выполняется запрос к базе данных для выборки путей из таблицы с роутами. По умолчанию menu_router.

473:     $parts = array_slice($original_map, 0, MENU_MAX_PARTS);
474:     $ancestors = menu_get_ancestors($parts);
475:     $router_item = db_query_range('SELECT * FROM {menu_router} WHERE path IN (:ancestors) ORDER BY fit DESC', 0, 1, array(':ancestors' => $ancestors))->fetchAssoc();

В таблице хранятся колбэки, которые нужно выполнять при обращении к соответствующим путям.

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

Вариант 1. Оформи подписку на «Хакер», чтобы читать все материалы на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи один материал

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


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

Check Also

Эхо кибервойны. Как NotPetya чуть не потопил крупнейшего морского перевозчика грузов

Российское кибероружие, построенное на утекших у АНБ эксплоитах, маскировалось под вирус-в…