В знаменитом форумном движке phpBB обнаружилась уязвимость, связанная с PHP-десериализацией. В результате некорректной проверки настроек атакующий может сохранить файл с произвольным содержимым на целевой системе и таким образом получить выполнение произвольных команд и скомпрометировать сервер. Здесь я детально разберу каждый аспект обнаруженной проблемы и покажу способ ее эксплуатации.
Уязвимость связана с техникой PHAR-десериализации, которую мы недавно освещали. Баг актуален для phpBB версии 3.2.3.
Думаю, что phpBB в представлении не нуждается. Он существует аж с 16 декабря 2000 года. Поднимем бокалы за его совершеннолетие! За это время движок повидал множество уязвимостей самого разного рода. Одна из самых известных — CVE-2004-1315, или в миру viewtopic highlight PHP injection. Этот баг был одним из первых, который я изучил.
Впрочем, вернемся к современным реалиям. Сейчас phpBB, конечно, растерял былую популярность, но все еще огромное количество площадок выбирает его как основную платформу общения пользователей.
Уязвимость нашел исследователь из RIPS Technologies, воспользовавшись сканером исходных кодов производства своей компании. Можно даже заценить его отчет.
Начнем с привычного — поднятия среды для тестирования уязвимости. Форум работает со многими базами данных, но я буду использовать старый добрый MySQL в виде контейнера Docker. Рекомендую использовать версии из ветки 5.х, так как в последних бранчах (8.х) изменился протокол авторизации по умолчанию и клиентские библиотеки текущих репозиториев PHP не работают с ним. Такое поведение можно поменять в конфигурационном файле MySQL, но зачем лишние телодвижения для тестового стенда, верно?
$ docker run -e MYSQL_USER="phpbb" -e MYSQL_PASSWORD="JaLdqX5on0" -e MYSQL_DATABASE="phpbb" -d --rm --name=mysql --hostname=mysql mysql/mysql-server:5.7
Теперь можно приступать к разворачиванию самого сервера. По традиции использую Debian.
$ docker run --rm -p80:80 -ti --name=phpbb --hostname=phpbb --link=mysql debian /bin/bash
Обновляем репозитории и ставим нужные пакеты.
$ apt update && apt install -y zip wget nano apache2 php php-mysql php-xml php-mbstring php-gd
Скачиваем архив с уязвимой версией форума phpBB (3.2.3) и распаковываем его.
$ cd /var/www/html $ wget https://www.phpbb.com/files/release/phpBB-3.2.3.zip $ unzip phpBB-3.2.3.zip $ chown www-data:root -R phpBB3
Если хочется побаловаться с отладкой, то дополнительно ставим xdebug
.
$ apt install -y php-xdebug
Настраиваем модуль и включаем его. Не забывай изменить IP под свои реалии.
$ echo "xdebug.remote_enable=1" >> /etc/php/7.0/mods-available/xdebug.ini $ echo "xdebug.remote_host=192.168.99.1" >> /etc/php/7.0/mods-available/xdebug.ini $ phpenmod xdebug
Далее правим конфиги веб-сервера и запускаем его.
$ sed 's/html/html/phpBB3/' -i /etc/apache2/sites-enabled/000-default.conf $ service apache2 start
Теперь переходим в браузере по адресу контейнера и устанавливаем и настраиваем форум.
После завершения инсталляции не забудь снести папку install
.
$ rm -rf /var/www/html/phpBB3/install
Стенд готов.
Начнем с просмотра исходников. Если ты внимательно изучал мою прошлую статью про PHAR-десериализацию, то знаешь, на вызовы каких функций стоит обратить особое внимание при поиске потенциально уязвимых мест. Конкретно в этом случае нужно поискать file_exists
. Код phpBB объемный (~300 тысяч строк), и вызовов этой функции там предостаточно. Но нас интересуют только те, которым в качестве аргумента можно пропихнуть юзердату. Не буду тянуть и скажу, что интересующий нас вызов находится в файле functions_acp.php
.
420: function validate_config_vars($config_vars, &$cfg_array, &$error) 421: { ... 428: foreach ($config_vars as $config_name => $config_definition) 429: { ... 443: switch ($validator[$type]) 444: { ... 544: case 'rpath': 545: case 'rwpath': ... 568: case 'absolute_path': 569: case 'absolute_path_writable': 570: // Path being relative (still prefixed by phpbb_root_path), but with the ability to escape the root dir... 571: case 'path': 572: case 'wpath': ... 588: $path = in_array($config_definition['validate'], array('wpath', 'path', 'rpath', 'rwpath')) ? $phpbb_root_path . $cfg_array[$config_name] : $cfg_array[$config_name]; 589: 590: if (!file_exists($path)) 591: { 592: $error[] = sprintf($user->lang['DIRECTORY_DOES_NOT_EXIST'], $cfg_array[$config_name]); 593: } 594: 595: if (file_exists($path) && !is_dir($path)) 596: { 597: $error[] = sprintf($user->lang['DIRECTORY_NOT_DIR'], $cfg_array[$config_name]); 598: } 599: 600: // Check if the path is writable 601: if ($config_definition['validate'] == 'wpath' || $config_definition['validate'] == 'rwpath' || $config_definition['validate'] === 'absolute_path_writable') 602: { 603: if (file_exists($path) && !$phpbb_filesystem->is_writable($path)) 604: { 605: $error[] = sprintf($user->lang['DIRECTORY_NOT_WRITABLE'], $cfg_array[$config_name]); 606: } 607: }
Из названия файла можно понять, что функция валидации конфигурационных переменных (validate_config_vars
) заходит в нужную нам ветку, когда выполняется проверка путей в панели администратора (в терминологии phpBB ACP — Administrator Control Panel).
Проверим это. Откроем админку и найдем любой раздел, где можно указать путь.
Как видишь, я открыл настройки прикрепленных файлов. Там есть опция Upload directory — папка, в которую они будут загружаться. Теперь поставим бряк где-нибудь в начале тела case
и нажмем Submit.
Брейк-пойнт сработал, так как валидатором переменной upload_path
служит wpath
.
138: $display_vars = array( ... 150: 'upload_path' => array('lang' => 'UPLOAD_DIR', 'validate' => 'wpath', 'type' => 'text:25:100', 'explain' => true),
Никаких дополнительных проверок переменной $path
не производится, и указанное пользователем значение попадает в качестве аргумента в функцию file_exists
.
Обрати внимание на добавленный префикс ./../
. Он появляется, потому что мы имеем дело с настройкой, которая подразумевает относительные пути. Но для выполнения атаки нам нужен полный контроль над всей переменной, поскольку требуется передать значение, начинающееся с враппера phar://
. Для этих целей отлично подойдут те настройки, у которых есть валидатор absolute_path
.
Одна из таких — это img_imagick
. Путь до бинарника утилиты ImageMagick для манипуляции с загруженными изображениями. Находится она там же, в разделе настройки аттачей.
138: $display_vars = array( ... 167: 'img_imagick' => array('lang' => 'IMAGICK_PATH', 'validate' => 'absolute_path', 'type' => 'text:20:200', 'explain' => true, 'append' => ' <span>[ <a href="' . $this->u_action . '&action=imgmagick">' . $user->lang['SEARCH_IMAGICK'] . '</a> ]</span>'),
Вот теперь получается настоящее внедрение, и первая часть атаки успешно выполнена.
Материалы из последних выпусков можно покупать отдельно только через два месяца после публикации. Чтобы продолжить чтение, необходимо купить подписку.
Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке
1 год7190 р. Экономия 1400 рублей! |
1 месяц720 р. 25-30 статей в месяц |
Уже подписан?
Читайте также
Последние новости