Ты наверняка неоднократно видел форумы на движке vBulletin. Форумы как таковые уже не на пике моды, но vBulletin по-прежнему один из самых популярных движков. В его последней (пятой) версии нашли несколько уязвимостей, которые способны сильно испортить жизнь админу. В этой статье я расскажу, как они эксплуатируются.

Первая проблема заключается в некорректной фильтрации пользовательских данных. О ней сообщил независимый исследователь безопасности, который пожелал остаться анонимным. Уязвимость, хоть и имеет некоторые ограничения, получила статус критической, потому что позволяет читать любые файлы и выполнять произвольный код на целевой системе.

Вторая уязвимость была найдена исследователями из компании TRUEL IT и получила идентификатор CVE-2017-17672. Она связана с особенностями десериализации данных в движке и может быть использована атакующим для удаления произвольных файлов в системе.

Полные отчеты с деталями обеих проблем были опубликованы в рамках программы Beyond Security от SecuriTeam. Там же есть PoC-эксплоиты для демонстрации уязвимостей. Давай по порядку пройдемся по всему этому.

 

Приготовления

Поделиться рабочим стендом в виде контейнера Docker я, к сожалению, не могу: из-за особенностей одной из уязвимостей успешная эксплуатация зависит от системы, а ОС, на которой она возможна, — это Windows. К тому же vBulletin — штука платная, и тебе придется самостоятельно найти возможность достать дистрибутив, если хочешь сам пощупать уязвимости. Скажу только, что нужная нам версия — это 5.3.3.

В качестве сервера я использовал дистрибутив WAMP.

Инсталляция vBulletin 5
Инсталляция vBulletin 5
 

Читаем файлы, выполняем команды

Итак, причина первой уязвимости — некорректная логика при обработке параметра routestring, которая позволяет атакующему добавить через include любой файл на диске и выполнить PHP-код, который в нем находится.

Наш путь начинается с самого главного файла — index.php, где происходит базовая инициализация приложения.

/index.php
48: $app = vB5_Frontend_Application::init('config.php');
...
60: $routing = $app->getRouter();
61: $method = $routing->getAction();
62: $template = $routing->getTemplate();
63: $class = $routing->getControllerClass();

Посмотрим на метод vB5_Frontend_Application::init.

/includes/vb5/frontend/application.php
13: class vB5_Frontend_Application extends vB5_ApplicationAbstract
14: {
15:     public static function init($configFile)
16:     {
17:         parent::init($configFile);
18:
19:         self::$instance = new vB5_Frontend_Application();
20:         self::$instance->router = new vB5_Frontend_Routing();
21:         self::$instance->router->setRoutes();

Здесь нас интересует метод setRoutes.

/includes/vb5/frontend/routing.php
47:     public function setRoutes()
48:     {
49:         $this->processQueryString();
...
54:         if (isset($_GET['routestring']))
55:         {
56:             $path = $_GET['routestring'];

В переменную $path попадает значение юзердаты из параметра routestring. В него можно передать путь до страницы форума, и она будет загружена.

Передача параметра routestring
Передача параметра routestring

Допустим, мы передали /test.

После назначения переменной следует кусок кода, который избавляется от слеша в начале строки, если он присутствует.

/includes/vb5/frontend/routing.php
75:         if (strlen($path) AND $path{0} == '/')
76:         {
77:             $path = substr($path, 1); // $path = "test"
78:         }

Далее проверяется размер переданных данных, и если он больше двух, то начинается получение расширения. Вдруг ты решил передать что-то запрещенное!

includes\vb5\frontend\routing.php
83:         if (strlen($path) > 2 )
84:         {
85:             $ext = strtolower(substr($path, -4)) ;
86:             if (($ext == '.gif') OR ($ext == '.png') OR ($ext == '.jpg') OR ($ext == '.css')
87:                 OR (strtolower(substr($path, -3)) == '.js') )
88:             {
89:                 header("HTTP/1.0 404 Not Found");
90:                 die('');
91:             }
92:         }

Как видишь, проверка довольно странная. Как минимум смущает наличие зашитого прямо в код списка запрещенных расширений. Да и вообще сам факт, что расширение получают, вырезая четыре символа с конца строки (строка 85), вызывает недоумение. В общем, если мы пытаемся получить файл с расширениями gif, png, jsp, css или js, то сервер вернет страницу 404 и выполнение скрипта прекратится. Когда все проверки пройдены, с помощью callApi вызывается метод getRoute из класса vB_Api_Route. Он ищет подходящие роуты, исходя из переданной пользователем информации.

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

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

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

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

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


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии