Удалить нельзя эксплуатировать: как мы нашли уязвимость в установщике Битрикс
Методология тестирования на проникновение предполагает разделение поиска уязвимостей на несколько этапов. Один из первых этапов заключается в поиске легаси с известными уязвимостями, популярных мисконфигов и других low hanging fruits на периметре. Их эксплуатация проста и зачастую не требует от потенциального злоумышленника какой-либо квалификации, но последствия она может иметь серьезные, вплоть до получения удаленного доступа во внутреннюю инфраструктуру.
Так как тестирование на проникновение, как правило, ограничено временными рамками, подобные уязвимости проверяются в первую очередь, ведь наша задача — достичь максимального результата в минимальные сроки.
Таким образом, встречая Outlook на периметре, мы не мчим сломя голову искать в нем 0-day, если на соседнем хосте висит устаревший сервис с публично доступным эксплойтом на Github.
Это был тот случай, когда анализом самописного приложения или использованием готового модуля для Metasploit было не обойтись. Большинство внешних сервисов клиента находились под аутентификационным порталом Битрикс24, так что поверхность атаки была крайне мала, и, на первый взгляд, эксплуатировать было нечего. Тем не менее, перед тем, как попытаться получить доступ посредством фишинга, мне хотелось найти какой-то веб-артефакт, который мог бы использоваться в тексте рассылки, например, Reflected File Download для ссылки в письме или XSS для кражи сессий сотрудников.
Мое внимание привлек скрипт bitrixsetup.php
на одном из хостов.
При обнаружении этого скрипта основной вектор атаки, который приходит в голову, это переустановить CMS и загрузить веб-шелл через административную панель. Но в нашем случае был нюанс: у скрипта отсутствовали права на запись в эту директорию. Разработчики Битрикс рекомендуют удалять данный скрипт после установки, но системные администраторы, видимо, посчитали, что ограничить права на запись будет достаточно. В процессе поиска информации об этом скрипте также обнаружилось, что он доступен для скачивания на официальном сайте и не требует для этого какого-либо взаимодействия с менеджерами Битрикс.
Все эти обстоятельства подбивали рискнуть и погрузиться в детальный ресерч, даже несмотря на то, что успех не был гарантирован.
XSS
Первое, что бросилось в глаза в процессе использования скрипта, это подозрительный параметр filename
:
/bitrixsetup.php?action=UNPACK&by_step=Y&filename=en_bitrix24_encode.tar.gz&lang=en&xz=19279
Если в этот параметр поместить произвольную строку, то обнаружится, что ошибка отображается на странице без какого-либо экранирования, что позволяет нам попробовать добавить XSS-пейлоад:
Вывод диалогового окна, свидетельствующее об успешном исполнении внедренного кода
HTML-контекст, в который встраивается пейлоад
Если бы установщик Битрикс соседствовал с другим веб-приложением, это можно было бы использовать для угона cookie с неправильно настроенными атрибутами, обхода SOP и прослушивания сообщений postMessage. В нашем случае других веб-приложений на этом хосте обнаружено не было.
Что привело к такому поведению? Пользовательский ввод никак не обрабатывается, он передается в конструктор CArchiver
в исходном виде:
//...
elseif ($strAction=="UNPACK")
{
SetCurrentStatus(LoaderGetMessage("LOADER_UNPACK_ACTION"));
$oArchiver = new CArchiver($_SERVER["DOCUMENT_ROOT"]."/".$_REQUEST["filename"], true);
//...
}
Где он попадает в атрибут $_strArchiveName
…
//...
class CArchiver
{
var $_strArchiveName = "";
//...
function __construct($strArchiveName, $bCompress = false)
{
$this->_strArchiveName = $strArchiveName;
//...
}
//...
}
И без какого-либо экранирования выводится на странице в сообщении об ошибке:
//...
function _openRead()
{
//...
if (!$this->_dFile)
{
$this->_arErrors[] = array("ERR_OPEN", "Unable to open '".$this->_strArchiveName."' in read mode");
return false;
}
//...
}
function GetErrors()
{
return $this->_arErrors;
}
//...
$arErrors = $oArchiver->GetErrors();
if (count($arErrors)>0)
{
if ($ft = fopen($_SERVER["DOCUMENT_ROOT"]."/".$this_script_name.".log", "wb"))
{
foreach ($arErrors as $value)
{
$str = "[".$value[0]."] ".$value[1]."\\n";
fwrite($ft, $str);
$txt .= $str . '
';
}
fclose($ft);
}
}
Зачем передавать имя установочного файла на клиентскую сторону? После чтения кода мы убедились, что пользовательский ввод никак не обрабатывается, а $_REQUEST["filename"]
передается объекту $oArchiver
, ответственному за разархивирование этого файла. Несмотря на наличие архиватора, закрадывается мысль о наличии LFR.
Local File Read
ℹ️ LFR, или Local File Read, это уязвимость, позволяющая злоумышленнику читать произвольные файлы с сервера, на котором запущенно уязвимое ПО. Не следует путать с уязвимостью Local File Inclusion (LFI), так как в этом случае файлы включаются через специфичную для PHP конструкцию include, что позволяет не только читать их, но и исполнять в контексте веб-приложения.
Недолго думая, я передаю заветную последовательность ../../../../etc/passwd
в параметр filename
и… Ничего не происходит.
С этого момента меня начала засасывать кроличья нора. Я долго читаю код, стараясь понять, как мне вытащить с сервера конфигурационный файл. Меняю кодировки в изначальном пейлоаде ../../../../etc/passwd
, добавляю и убираю слэши, вставляю нульбайты и пробельные символы. Брейншторм с товарищами из лаборатории приводит меня к тестированию таких эзотерических вещей, как php://
фильтры. Ничего не работает.
Погуляв по улице и потрогав траву, я задаюсь вопросом:, а что же видно со стороны сервера, когда я отправляю ему все эти пейлоады, и разворачиваю bitrixsetup.php
в докере. Проверку начинаю с изначального GET-запроса:
http://localhost:8000/bitrixsetup.php?action=UNPACK&filename=../../../../etc/passwd
Невероятно, но локально LFR отрабатывает:
Приложение вывело первую сточку /etc/passwd
Думаю, вы можете представить мое лицо в этот момент:
Судя по всему, мой пейлоад «ломался» до того, как достигал своей цели из-за специфической настройки сервера клиента. Об этом свидетельствовало сообщение об ошибке:
Файл urlrewrite.php используется для того, чтобы скрипт, находящийся по одному адресу, мог отвечать на другом
По крайней мере локально у меня было целых, ну, скажем, полторы уязвимости! В принципе, неплохой результат для исследования, лишь косвенно относящегося к основной задаче пентеста.
Почему же пейлоад заработал сейчас?
Выяснилось, что в силу того, что часть файла по указанному пути считывается скриптом как заголовок архива, мы можем прочитать его в сообщении об ошибке. Кроме того, обнаружилось, что используя параметр seek
, возможно посимвольно сдвигать строку читаемого файла:
Приложение вывело 10-ую строчку файла /etc/passwd при значении seek=327
Вот только дальше 10-ой строки /etc/passwd
в Alpine или значения seek=327
мы получаем другую ошибку, в которой содержимое файла прочитать уже не удается.
Фактическое содержание файла в докере и последняя строка, до которой его возможно прочесть
Какой импакт может иметь частичная LFR, способная прочитать только первые 10 строк файла?
У продуктов Битрикс существует стандартный конфигурационный файл /bitrix/php_interface/dbconn.php
. Выглядит этот файл так:
//ваши данные
$DBLogin = "login";
$DBPassword = "pass";
$DBName = "database";
$DBHost = "localhost";
define("DBPersistent", false);
$DBType = "mysql";
$DBDebug = false;
$DBDebugToFile = false;
// ...
?>
Получается, что для того, чтобы прочитать логин и пароль от базы данных, 10 строк будет вполне достаточно. Это позволяет осуществить горизонтальное перемещение на хосте из уязвимого веб-приложения в базу данных, что является уже довольно серьезным последствием эксплуатации.
Я решила поискать аналогичные мисконфиги, используя следующий Google dork:
inurl:/bitrixsetup.php?lang=
Тогда мне удалось найти около 30 различных ресурсов в кэше поисковика. Не все из них были доступны, но, несмотря на это, нашлись живые веб-приложения с доступным файлом bitrixsetup.php
. Значит, нужно было писать вендору с сообщением о найденных уязвимостях.
Раскрытие уязвимости
На тот момент как раз недавно вышли CVE-2023–1713, CVE-2023–1714, CVE-2023–1715, CVE-2023–1717 и еще несколько других идентификаторов уязвимостей от исследователей компании StarLabs. Ознакомившись с их разделом Advisories, мне удалось узнать, что для прямого контакта с отделом информационной безопасности Битрикс лучше всего использовать форму по адресу https://www.bitrix24.com/report.
Несмотря на то, что сотрудники StarLabs остались недовольны коммуникацией с Битрикс, сотрудничество в нашем случае оказалось плодотворным и продуктивным. Мне ответили оперативно — через две недели. Установочный скрипт изначально не входил в их модель угроз (ведь ответственные пользователи должны удалять его), но коллеги из Битрикс согласились принять уязвимость и начали исправление. С учетом новогодних праздников это произошло довольно быстро, и 20-ого февраля они опубликовали информацию об уязвимости и выпустили обновление: https://www.1c-bitrix.ru/vul/20326726/. С их разрешения я пишу и публикую эту статью.
Приятным бонусом оказалось добавление уязвимости в БДУ под идентификатором BDU:2024–01501, несмотря на то, что про XSS или даже про «отсутствие экранирования сообщения об ошибке, содержащее пользовательский ввод» там не упомянуто.
Заключение
Вся эта история в очередной раз подчеркивает важность регулярной проверки даже широко используемых, и, казалось бы, множество раз проверенных программных продуктов. Ведь опыт любого пентестера, ровно как и конфигурация инфраструктуры и ПО у каждого клиента уникальны. Иногда имеет смысл уделить пару-тройку часов на самостоятельное исследование даже если вы не senior red team operator или гуру bug bounty.
Несмотря на то, что выявленные уязвимости не получилось использовать при проведении социальной инженерии, фишинговая атака была успешной и позволила пробить периметр и попасть во внутреннюю сеть.
А благодаря этому ресерчу нам удалось сделать рунет хоть чуточку, но безопаснее.