Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Leaky Vessels – nowa seria podatności w konteneryzacji
Słowo kontener w IT jednoznacznie kojarzy się z dockerem chociaż nie jest to jedyna technologia pozwalająca na opakowanie oprogramowania w paczkę, która zawiera niezbędne do uruchomienia środowisko (czyli zależności, biblioteki). Kontenery mogą korzystać np. z namespaces i cgroups, aby odizolować uruchamiane programy od siebie nawzajem. Współdzielenie kernela systemu bazowego powoduje, że jest to rozwiązanie lekkie i szybkie, konfigurowane na dostępnych obrazach systemowych przy pomocy plików tekstowych, co ułatwia ich wersjonowanie. Kontenery przyjęły się szczególnie w środowiskach chmurowych, gdzie zarządzanie i instrumentacja klastrów kontenerów odbywa się przy pomocy np. Kubernetesa.
Opublikowane zostały w dniu 31.01.2024 na blogu Snyka cztery podatności:
- CVE-2024-21626
- CVE-2024-23651
- CVE-2024-23653
- CVE-2024-23652
Dotyczą kluczowych elementów do budowy i zarządzania infrastrukturą skonteneryzowanych aplikacji, a nie samych obrazów i skutkują możliwością wyjścia (wyskoczenia) ze środowiska kontenera do systemu operacyjnego hosta.
CVE-2024-21626
Podatność CVE-2024-21626 dotyczy bezpośrednio runc, czyli narzędzia CLI służącego do uruchamiania kontenerów (tzw. container runtime). Ucieczka do hosta możliwa jest przez wykorzystanie złośliwego pliku Dockerfile albo spreparowanego obrazu systemu (upstream image), który zostanie wybrany za pomocą słowa kluczowego FROM
.
Powodem wystąpienia tego problemu jest kolejność operacji przy zastosowaniu dyrektywy WORKDIR
, która definiuje początkowy katalog roboczy wszystkich procesów utworzonych w Dockerfile’u, takich jak te powstałe na skutek użycia innych dyrektyw – na etapie budowania kontenera (słowo kluczowe RUN
) oraz na etapie działania kontenera (dyrektywy CMD
oraz ENTRYPOINT
). Okazuje się, że zdefiniowany katalog jest odwiedzany przy użyciu syscalla chdir jeszcze zanim specyficzne, uprzywilejowane deskryptory plików hosta zostaną zamknięte. To powoduje, że możliwe jest wykorzystanie tych uprzywilejowanych deskryptorów przez odwołanie się do katalogu /proc/self/fd/
jako argumentu wywołania chdir
. Dzięki temu deskryptory te pozostają dostępne nawet po wykonaniu operacji zamknięcia, jeszcze przed przekazaniem sterowania do komend zawartych w Dockerfile.
Atak ma kilka wersji, możliwe jest wykorzystanie wywołań runc init
czy też runc exec
.
Jak to działa w praktyce? Złośliwie skonfigurowany kontener spowoduje, że startujący wewnątrz proces będzie miał dostęp do systemu plików hosta. Domyślnie uprawnienia procesu będą identyczne z tymi, jakich używa silnik np. Docker Engine albo K8s (Kubernetes) – gdzie zwykle operują one jako root, stąd też możliwe jest wykorzystanie dostępu do dysku w celu osiągnięcia RCE w kontekście roota na hoście.
Problem został rozwiązany przez deweloperów runc
poprzez sprawdzenie, czy ścieżka zdefiniowana przez WORKDIR
znajduje się wewnątrz filesystemu kontenera. Dodatkowo zaimplementowano mechanizmy, mające na celu poprawne obsłużenie zamknięcia deskryptorów plików hosta po ich użyciu.
W notce informującej o podatności, autorzy wskazują, że wydanie oznaczone numerem 1.1.12 zawiera wymagane łatki i zarówno samo runc, jak i rozwiązania korzystające z tego narzędzia powinny zostać zaktualizowane, tak aby używać poprawionej wersji.
Snyk udostępnił też, zbudowane na filtrach eBPF, narzędzie o nazwie leaky-vessels-runtime-detector
, pozwalające zidentyfikować uruchomione kontenery, które próbują wykorzystać tę podatność. Możliwe jest też wykorzystanie statycznego analizatora, który sprawdzi bazowe obrazy użyte z instrukcją FROM
pod kątem wykorzystania słów kluczowych WORKDIR
i ONBUILD
, które będą nosiły znamiona złośliwych. Ta druga metoda została określona jako mniej precyzyjna i powodująca dużo fałszywych alarmów.
Przed opublikowaniem znaleziska, badacze przeszukali popularne rejestry obrazów i nie znaleźli dowodów na to, że te podatności były wykorzystywane. Sami jednak podkreślają, że ich research nie był wyczerpujący i sytuacja ta może się zmienić, zwłaszcza po upublicznieniu znaleziska.
Wskazano również potencjalne problemy z innymi środowiskami wykonania jak crun czy youki, gdzie zauważono podobne błędy chociaż jak na razie nie udało się ich wyeksploitować.
CVE-2024-23651
Podatność oznaczona numerem katalogowym CVE-2024-23651, wykorzystuje z kolei mechanizm podmontowywania wolumenów typu cache podczas budowania kontenera. Funkcjonalność definiowana przez dyrektywę RUN --mount=type=cache
pozwala na montowanie katalogu ze stałą zawartością podczas procesu budowania obrazu, ma to na celu poprawienie wydajności poprzez zachowywanie cache pomiędzy kolejnymi procesami budowania.
Luka to błąd klasy race-condition. Wyścig występuje między walidacją a użyciem (tzw. TOCTOU – time-of-check/time-of-use) ścieżki wewnątrz podmontowanego cache. Możliwa jest podmiana tej ścieżki pomiędzy sprawdzeniem a wywołaniem systemowym mount, co powoduje że możliwe jest podmontowanie dowolnego katalogu wewnątrz filesystemu kontenera. Analogicznie do poprzedniej podatności, w momencie wykonania ataku, podmontowywany jest filesystem hosta. Uprawnienia będą identyczne jak samego procesu silnika Dockera, czyli użytkownika root. To oczywiście pozwala na eskalację dostępu do dysku do wykonania poleceń z uprawnieniami roota.
CVE-2024-23653
Nieco inne podejście wykorzystano przy eksploitacji CVE-2024-23653. Problem wynika z brakującego sprawdzenia uprawnień przez endpoint GRPC (framework do zdalnych wywołań procedur) wykorzystywany przez BuildKit (backend, który m.in. przyspiesza budowanie kontenerów). Jedna z funkcji Dockera pozwala na definicję własnego parsera formatów przy pomocy dyrektywy # syntax=
umieszczonej na samej górze Dockerfile. Można tam wskazać inny obraz dockerowy, który będzie wykorzystany do przetransformowania wejścia do pośredniej reprezentacji. Zdefiniowany parser ma możliwość wywołania wielu metod na serwerze GRPC. Jednym z dostępnych endpointów jest Container.Start
, który pozwala na uruchamianie kontenerów. Przy wywołaniu nie jest sprawdzany parametr StartRequest.SecurityMode
, co pozwala na uruchomienie przez parser kolejnego kontenera z podniesionymi uprawnieniami, a to w efekcie prowadzi do ucieczki z ograniczonego środowiska i oczywiście wykonanie poleceń jako root na bazowym systemie.
CVE-2024-23652
Ostatnią luką z opublikowanego zestawu jest CVE-2024-23652, która również dotyka Buildkita, a konkretnie etapu usuwania tymczasowych katalogów po ich użyciu. W przypadku podania w dyrektywie RUN --mount
nieistniejącego katalogu, zostanie on stworzony na potrzeby operacji budowania, a następnie usunięty, gdy przestanie być potrzebny. Okazuje się, że jeśli katalog nadrzędny zostanie podmieniony na dowiązanie symboliczne w czasie gdy dany kontener będzie działał, to operacja czyszczenia przed jego zamknięciem spowoduje wykorzystanie dowiązania symbolicznego. Pozwala to na usunięcie wskazanego pliku z systemu hosta. Ponieważ Buildkit, tak samo jak engine działa z uprawnieniami roota, to możliwe jest usunięcie dowolnego pliku. Poprawiona wersja 0.12.5 jest już jednak dostępna.
Autorzy badania zalecają aktualizację komponentów środowiska uruchomieniowego kontenerów, ponieważ zawierają one wszystkie potrzebne na ten moment łatki. Należy podkreślić, że aby doszło do skutecznej eksploitacji, to uprzywilejowany użytkownik musi zostać skłoniony do wykonania określonej operacji (np. skorzystania ze złośliwego pliku Dockerfile), czyli wymagany jest element socjotechniki/ataku na łańcuch dostaw, aby taki Dockerfile/obraz został uruchomiony.
~fc