Содержание статьи
Подготовка
Для демонстрации уязвимости проще всего использовать официальный репозиторий 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 и проверяем, все ли у нас работает.
Еще неплохо было бы завести отладчик. Для этого я дополнительно установлю расширение 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-й порт — и вперед, к победам.
Первые шаги
Перейдем на страницу создания нового аккаунта. В седьмой версии она значительно аскетичнее, чем в восьмой.
Из-за того, что отсутствует возможность загрузить аватар, стандартный вектор эксплуатации тут не сработает. Значит, нужно найти новый! Суть бага все та же — это внедрение элементов в 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
.
/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. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»