Odcisk palca serwerów API Kubernetes

https://chacker.pl/

Sercem systemu Kubernetes jest serwer API. Serwer API jest używany do większości komunikacji między komponentami systemu. Operatorzy będą używać serwera API do pracy z różnymi komponentami. Węzły będą używać API do komunikacji z płaszczyzną sterowania. API jest swego rodzaju obwodem bezpieczeństwa; każda błędna konfiguracja komponentów serwera API doprowadzi do naruszenia bezpieczeństwa systemu. Przyjrzymy się uważnie temu komponentowi w ciągu kilku następnych laboratoriów.

Architektura Kubernetes

https://chacker.pl/

Zrozumienie całości architektury Kubernetes pomoże Ci przeanalizować jej słabości. Pierwszym elementem będzie zrozumienie płaszczyzny sterowania. Płaszczyzna sterowania samego Kubernetes składa się z kontenerów wymienionych poniżej.

UWAGA: Płaszczyzna sterowania systemu to płaszczyzna lub strefa, w której system działa poza swoimi standardowymi obciążeniami. Ta strefa odpowiada za zaplecze systemu i organizuje komponenty, z których składa się system. Płaszczyzna, z którą użytkownik wchodziłby w interakcję w przypadku obciążenia w Kubernetes, takiego jak aplikacje internetowe hostowane w Kubernetes, byłaby nazywana płaszczyzną danych.

  • Serwer API Serwer API jest sercem systemu i jest integralną częścią komunikacji między węzłami Kubernetes a komponentami Kubernetes. Wszystkie komponenty wchodzą w interakcję za pośrednictwem serwera API. Obejmuje to zarówno komponenty wewnętrzne, jak i zewnętrzne. Operatorzy i administratorzy używają serwera API, a poszczególne kontenery również go używają.
  • Etcd Jest to magazyn klucz/wartość, który zawiera bazę danych dla komponentów płaszczyzny sterowania. Jest to bezplikowy odpowiednik katalogu /etc w systemie operacyjnym Unix. Etcd jest również podstawowym komponentem systemu, a każda interakcja API żądana przez serwer API może zostać zapisana w Etcd, aby inne komponenty mogły ją odczytać i wykonać jako żądanie.
  • kube-scheduler Jest to system planowania; utrzymuje działanie kontenerów. Kube-scheduler sprawdza, co powinno być uruchomione, jak powinno być uruchomione i czy powinno być uruchomione, a następnie zapewnia wykonanie tych operacji.
  • kube-controller-manager Jest to seria kontrolerów, które utrzymują różne komponenty operacyjne. Każdy kontroler ma określone zadanie, a menedżer je organizuje.
  • cloud-controller-manager Cloud-controller-manager jest abstrakcją dla każdej chmury, co pozwala Kubernetesowi działać w różnych dostawcach chmury lub systemach lokalnych. Na przykład możliwość pracy z Elastic Load Balancers w EC2 w porównaniu z Google Load Balancers nie jest wpisana w rdzeń produktu; zamiast tego jest abstrakcyjna w tej warstwie cloud-controller-manager.

Inne komponenty w płaszczyźnie sterowania to komponenty, które znajdują się na każdym indywidualnym węźle. Węzły uruchamiają następujące komponenty na warstwie płaszczyzny sterowania:

  • Kubelet Agent Kubernetes, który komunikuje się z powrotem z serwerem API Kubernetes.
  • Kube-proxy Narzędzie do przekierowywania portów, które jest podobne do przekierowywania portów SSH.

Umożliwia to operatorowi systemu komunikowanie się z poszczególnymi kontenerami, które są wewnętrznie dostępne w klastrze

Rysunek  przedstawia diagram komponentów architektury Kubernetes.

Hackowanie Kubernetesa

https://chacker.pl/

Omówimy jedną z najnowszych technologii, być może najgorętszą technologię, jaka pojawiła się od czasu rozpoczęcia wirtualizacji. Jeśli kontener jest odpowiednikiem maszyny wirtualnej w swoim zastosowaniu, to Kubernetes jest odpowiednikiem systemu VMware vSphere. Kubernetes zmienił sposób, w jaki wiele organizacji mogło pakować i dostarczać oprogramowanie. Co dziwne, większość dystrybucji i systemów Kubernetesa nie jest wdrażana na gołym metalu. Zazwyczaj są wdrażane na istniejącym sprzęcie wirtualizacyjnym, chociaż bare metal jest obsługiwany. Podstawowym wymogiem dla Kubernetesa jest zwykle system Linux, ale obsługa Windows Worker staje się coraz bardziej dostępna. Przy całym szumie wokół architektur natywnych dla chmury, Kubernetes będzie systemem zarządzania, z którym możesz mieć do czynienia. Z każdą kolejną wersją może być coraz trudniej skutecznie go wykorzystać. Wykorzystamy kilka podstawowych technik ataków Kubernetesa, biorąc pod uwagę konteksty obronne i pułapki.

Nadmierne wykorzystywanie grup C

https://chacker.pl/

Co jeśli znajdziemy się w środowisku, w którym mamy możliwość wykonywania poleceń, ale nie mamy bezpośredniego dostępu do procesu Docker? Może obrazek by pomógł; Rysunek  przedstawia ścieżkę ataku, w której możemy uciec z kontenera za pomocą prostej sekwencji ucieczki.

Tutaj znajdujemy pewną lukę w kontenerze i możemy wykonywać polecenia w kontenerze. Przyjrzyjmy się sekwencji ucieczki kontenera, która została po raz pierwszy ujawniona przez Felixa Wilhelma11 i demonstruje prosty problem z montowaniem chroot. Kontynuując poprzednie zajęcia laboratoryjne, obecnie znajdujemy się w kontenerze o nazwie target_web_1 na zdalnej instancji Dockera. Stąd uruchomimy zlokalizowany exploit przy użyciu potomnych grup cgroups, które umożliwiają nam wykonywanie poleceń systemu operacyjnego na hoście:

Pierwsze polecenie ustawia katalog na /sys/fs/cgroup/rdma (1), czyli katalog cgroups v1 odwołujący się do bezpośredniej pamięci współdzielonej przez wszystkie kontenery. Stąd tworzony jest katalog o nazwie w, który utworzy kolejną potencjalną grupę cgroup (tę po prostu nazywa się x (2). Grupy cgroup w v1 można zagnieżdżać według typu, a nie według procesu, więc utworzenie folderu w tym miejscu tworzy potencjalną nową grupę cgroup, po prostu bez odniesień. Flaga notification_on_release (3) informuje jądro, że wykona ostatnie polecenie wymienione w pliku release_agent. Jeśli możemy kontrolować plik release_agent, mamy wykonanie polecenia na hoście. Rozważmy, w jaki sposób jądro musiałoby znaleźć tę lokalizację: musiałoby znać nie lokalizację chroot pliku, ale rzeczywistą lokalizację pliku. Jak znaleźć lokalizację ostatniego polecenia cmd do uruchomienia? Ostateczny katalog roboczy hosta to ostatnia warstwa w systemie OverlayFS, do której zwykle odwołuje się katalog diff. Możemy go znaleźć, używając pliku mtab, aby zlokalizować lokalizację naszego systemu plików nakładki (4). Gdy już to wiemy, mamy kilka dodatkowych przeszkód. Po pierwsze, potrzebujemy lokalizacji do przechowywania naszych danych wyjściowych wewnątrz kontener; pomyśl o tym jako o sposobie wysyłania wiadomości z hosta do jądra. Możemy użyć do tego pliku (w naszym przypadku /output (5)). Następnie musimy poinformować release_agent o pliku do uruchomienia. W naszym przykładzie /cmd (6) to polecenie do uruchomienia, a /output przechowuje nasze dane wyjściowe. Ostatnim krokiem jest uczynienie polecenia wykonywalnym i poinformowanie cgroup, którą utworzyliśmy w /w, aby zakończyła działanie. (7) Możemy to zrobić, przechowując 0 w /w/cgroup.procs, co instruuje jądro, że cgroup może teraz zakończyć działanie. Możemy nawet odczytać zawartość tego wyjścia po zatrzymaniu się na 1 sekundę, aby umożliwić systemowi hosta wykonanie zadania (8). Dlaczego jest to możliwe? Jak możemy wykonać te polecenia? Przede wszystkim system cgroups v1 musi być na miejscu, co będzie miało miejsce przez jakiś czas, ponieważ jądro Linux nie wprowadziło cgroups v2 do wersji jądra 4.5 i nie było w głównej dystrybucji do Fedory 31. Ubuntu 18.04 LTS i 20.04 LTS nadal używają cgroups v1, podobnie jak wiele dystrybucji RedHat, które są nadal używane. Następną rzeczą, której potrzebujemy, jest flaga –privilege lub możliwości jądra, które umożliwiają montowanie. To tylko jeden z wielu przykładów, w których atak jądra może doprowadzić do naruszenia bezpieczeństwa systemu. Czy kiedykolwiek widziałeś jakieś exploity jądra w środowisku naturalnym, które mogą być również narzędziem tego?

Podsumowanie

Kontenery są używane jako mechanizm wspomagający skalowanie architektur i zapewniający odporność oprogramowania. Kontenery wymieniają bezpieczeństwo na wydajność operacyjną, a ten rozdział podkreśla wiele luk, na które możemy natrafić w środowisku naturalnym. W tym rozdziale omówiono komponenty znajdujące się w kontenerach i sposoby ich potencjalnego wykorzystania. Następnym razem, gdy znajdziesz się w stosie aplikacji, pierwszą rzeczą, którą należy sprawdzić, może być to, czy znajdujesz się w środowisku kontenerowym. Możesz mieć możliwość ucieczki z tego środowiska i przejścia na zewnątrz do innych części architektury z mniejszym wysiłkiem, niż początkowo sądzono.

Uprzywilejowane kontenery

https://chacker.pl/

Uruchommy kontener Docker przy użyciu polecenia privileged i przekazując urządzenia od hosta.

W wielu dzisiejszych środowiskach gniazdo Dockera jest wystawione na ogólną sieć. Gniazdo Dockera, poza produktem Enterprise Swarm, nie jest w żaden znaczący sposób zabezpieczone. Jeśli możemy podłączyć się do gniazda Dockera, możemy wykonywać polecenia tak, jakbyśmy byli na samym hoście. Możemy użyć bardzo małego, lekkiego kontenera, takiego jak kontener BusyBox, jako przyczółka w naszym ataku (1), a nawet możemy użyć dodatkowych poleceń, aby umożliwić nam zamontowanie partycji / hosta w kontenerze jako /host. Korzystanie z BusyBox ma kilka zalet, z których jedną jest to, że samo nie wywoła żadnych alarmów. Teraz, gdy uruchomiliśmy kontener BusyBox z uprawnieniami, możemy zacząć używać powłoki do wykonywania poleceń tak, jakbyśmy byli na samym hoście. Po uruchomieniu kontenera możemy sprawdzić określone uprawnienia, takie jak użycie polecenia ps (2), aby wyświetlić listę procesów całego systemu. Ujawniłoby to inne kontenery, a nawet lokalizację dysku naszego własnego kontenera. Stąd możemy chrootować partycję /host (3), aby uczynić katalog główny naszego kontenera katalogiem głównym hosta. Możemy użyć system bash (4), a nawet uruchomić polecenia na hoście, tak jakbyśmy nie byli w samym kontenerze. Możemy udowodnić pełny dostęp w tym momencie, uruchamiając polecenia takie jak systemctl, co pozwoli nam wyświetlić wszystkie procesy na (5). Aby uzyskać zdalny dostęp do systemu, możemy dodać lokalnego użytkownika, do którego możemy się zalogować (6). Możemy upewnić się, że przejdziemy przez wszystkie domyślne pytania (7). Aby przetestować dostęp z powrotem do systemu, możemy teraz wyjść z powrotem (8). Stąd możemy zalogować się bezpośrednio do hosta Dockera, jeśli zajdzie taka potrzeba.

Możliwości

https://chacker.pl/

Jądro Linux ma wbudowany zestaw możliwości, które można włączyć, aby umożliwić bardziej szczegółową kontrolę tego, co użytkownik ma prawo lub nie może robić. Jednym z typowych przykładów, na jakie możesz się natknąć, jest korzystanie z popularnego narzędzia Wireshark jako użytkownik inny niż root. System poprosi Cię o włączenie następujących możliwości:

  • CAP_NET_RAW Użyj gniazd RAW i PACKET
  • CAP_NET_ADMIN Umożliwia administrowanie możliwościami sieciowymi, takimi jak konfiguracje interfejsu

Docker ma specjalną flagę, która jest uruchamiana w celu wyłączenia wszystkich kontrolek: flagę –privilege. Ta flaga wyłącza profile AppArmor10 dla kontenera, a także wyłącza ograniczenie możliwości. Nie jest nierozsądne znalezienie warunków w odwrotnych serwerach proxy i innych systemach pośredniczących, w których ta flaga jest uruchamiana w kontenerze.

Wychodzenie z kontenerów

http://chacker.pl

Wychodzenie z kontenerów jest powszechną formą konwersji w wielu kręgach i jest ku temu bardzo dobry powód. Możesz pamiętać, że hiperwizory zapewniają duży poziom ograniczeń, ponieważ sprzęt jest punktem granicznym dla maszyn wirtualnych. Kompromisem w przypadku kontenerów jest to, że jądro jest współdzielone przez wszystkie kontenery, co czyniłoby samo jądro punktem granicznym między kontenerami. Jak prawdopodobnie możesz sobie wyobrazić, ten poziom izolacji procesów jest bezpieczny tylko wtedy, gdy nie istnieją żadne exploity jądra. Ograniczenia kontenera opierają się na współdzielonym systemie, co jest kompromisem bezpieczeństwa.

Pivoty

https://chacker.pl/

Możemy poruszać się bocznie w środowisku na kilka sposobów, w tym ustawiając przekierowania portów i proxy. Możemy również po prostu wyłączyć pliki binarne, aby pomóc nam poruszać się dalej w środowisku, dopóki nie będziemy musieli wykonać dodatkowego bezpośredniego pivotu.

Identyfikacja systemu operacyjnego kontenera będzie miała kluczowe znaczenie przy wyłączaniu pakietów systemu operacyjnego (1). Alpine jest bardzo popularnym systemem operacyjnym opartym na kontenerach ze względu na jego niewielki rozmiar. Rozumiejąc, że kontener działa w systemie Alpine, możemy użyć apk (2), menedżera pakietów Alpine, aby zainstalować pakiet Redis. Teraz mamy kilka komponentów do przemieszczania się poziomo w obrębie tego samego hosta i mamy powłokę na jednym kontenerze z jej zdolnością do komunikowania się z innym kontenerem na tym samym hoście. Te kontenery mogą się komunikować, ponieważ współdzielą te same informacje o grupie sieciowej cgroup.

Kontener Redis ma tylko jeden klucz z nazwą hitów. Redis może zawierać wszelkiego rodzaju informacje i wiele razy możemy sprawić, że Redis da nam powłokę backdoor w systemie. Czy możemy pójść dalej w naszym środowisku? Czy możemy dostać się do systemu operacyjnego hosta?

Wykonywanie poleceń zdalnie

https://chacker.pl/

Teraz połączymy się zdalnie z odsłoniętym gniazdem Docker i uzyskamy zdalną powłokę w systemie. Będzie to znajome dla czytelników, którzy korzystali z powłok netcat bind.

Polecenie Docker exec (1) pozwala nam wykonywać polecenia wewnątrz kontenera Docker. Używając -H, możemy skierować Dockera do określonego hosta. Używając flagi -i, możemy wchodzić w interakcję z kontenerem, a używając flagi -t, możemy wykonywać polecenia względem znacznika kontenera, który jest podany jako „targets_web_1”. Polecenie podane na końcu, /bin/sh, jest poleceniem, które zostanie uruchomione. Dlaczego nie /bin/bash? Ten kontener uruchamia Alpine Linux, który jest bardzo lekką dystrybucją powszechnie spotykaną w środowiskach kontenerowych. Chociaż nigdy nie możemy być pewni, jaka jest dystrybucja docelowa, wiemy, że nawet dystrybucje takie jak Alpine, które uruchamiają powłokę BusyBox, będą miały /bin/sh. Wiele plików binarnych nie jest dostępnych w kontenerach uruchamiających Alpine, domyślnie, w tym bash. Następna seria poleceń pozwala nam wykonać pewne podstawowe widoki wokół środowiska kontenerowego. Zauważ, że działasz jako root, co jest oznaczone zarówno przez monit # (2), jak i przez polecenie env, w którym HOME jest ustawione na /root. Uruchomienie env (3) może dać nam ładny widok na to, co działa w kontenerze. Czasami zmienne środowiskowe pomagają nam zrozumieć środowisko, ponieważ wiele sekretów i przydatnych artefaktów może być obecnych. W tym kontenerze nie ma niczego wyjątkowego, co moglibyśmy znaleźć w zmiennych środowiskowych. Mamy jednak kilka interesujących rzeczy do odnotowania:

  • Następujący ciąg HOSTNAME=: jest w 8-cyfrowym formacie szesnastkowym, co wskazuje na konwencję nazewnictwa kontenerów Docker
  • Ciąg PYTHON_VERSION= wskazuje, że kontener jest przeznaczony wyłącznie dla Pythona lub usługi opartej na Pythonie, takiej jak flask.

Jeśli przyjrzymy się wynikowi polecenia ps -ef (4), możemy również zobaczyć, że ten kontener uruchamia swój główny proces jako PID 1. Jeśli przypomnimy sobie, jak działają cgroups, możemy zajrzeć do /proc/1/cgroups (5), aby zobaczyć, jakie cgroups są mapowane na ten proces. W sekcji cgroups zauważymy mapowany kontener Docker, który jest skrótem SHA-256 ostatecznego scalonego dysku Overlay2. Polecenie mount (6) pokazuje nam również, jak ten dysk nakładki jest nakładany na system — co może się przydać:

Używając polecenia netstat, możemy poszukać ESTABLISHED (7) i istniejących połączeń, które mogą nie być bezpośrednio wystawione na pod. Możemy rozpoznać, że jeden z tych portów to port bazy danych Redis (8)/value. Kontenery są z natury w pewnym stopniu niezmienne; jednak dostosowują się do zmian w kontenerze po wykonaniu. Te zmiany są odrzucane podczas ponownego uruchamiania lub przebudowy kontenera. Możemy użyć systemu operacyjnego kontenera, aby wyłączyć wszelkie pliki binarne, których możemy potrzebować do przenoszenia tego urządzenia.

Interakcja z interfejsem API Dockera

https://chacker.pl/

Możemy użyć interfejsu Docker CLI do tworzenia kontenerów; jednak znacznie łatwiej jest użyć natywnego klienta, który ma już wszystkie wywołania skodyfikowane w interfejsie CLI. Będziemy używać naszej instancji wdrożonej w Kali w chmurze AWS, aby wykonać następujące części laboratorium:

Możemy uruchomić polecenie docker z flagą -H (1), aby umożliwić nam określenie zdalnego hosta. Na zdalnym celu widzimy dwa kontenery: targets_redis_1 (2) i targets_web_1 (3). Pierwszy kontener nie udostępnia żadnych portów głównemu interfejsowi hosta (4), ale drugi kontener udostępnia (5). W obecnej konfiguracji demon nie jest uwierzytelniany ani szyfrowany; dlatego każdy atakujący podsłuchujący na tych interfejsach będzie mógł zobaczyć te polecenia.