Luka w zabezpieczeniach roota w jądrze Linuksa i odmowa usługi w systemd

Badacze bezpieczeństwa z Qualys ujawnili szczegóły dwóch luk w zabezpieczeniach jądra Linuksa i menedżera systemu systemd. Luka w jądrze (CVE-2021-33909) umożliwia użytkownikowi lokalnemu wykonanie kodu z uprawnieniami roota poprzez manipulację silnie zagnieżdżonymi katalogami.

Niebezpieczeństwo związane z luką zwiększa fakt, że badaczom udało się przygotować działające exploity, które w domyślnej konfiguracji działają na Ubuntu 20.04/20.10/21.04, Debianie 11 i Fedorze 34. Należy zauważyć, że inne dystrybucje nie zostały przetestowane, ale teoretycznie również są podatne na problem i mogą zostać zaatakowane. Pełny kod exploitów ma zostać opublikowany po wszędzie wyeliminowanym problemie, ale na razie dostępny jest jedynie prototyp o ograniczonej funkcjonalności, powodujący awarię systemu. Problem występuje od lipca 2014 roku i dotyczy wersji jądra począwszy od wersji 3.16. Poprawka luki została skoordynowana ze społecznością i zatwierdzona do jądra 19 lipca. Główne dystrybucje wygenerowały już aktualizacje swoich pakietów jądra (Debian, Ubuntu, Fedora, RHEL, SUSE, Arch).

Luka wynika z braku sprawdzenia wyniku konwersji size_t na int przed wykonaniem operacji w kodzie seq_file, który tworzy pliki z sekwencji rekordów. Niezaznaczenie sprawdzenia może skutkować zapisami do bufora poza granicami podczas tworzenia, montowania i usuwania bardzo zagnieżdżonej struktury katalogów (rozmiar ścieżki większy niż 1 GB). W rezultacie osoba atakująca może uzyskać 10-bajtowy ciąg „//usunięty” zapisany z przesunięciem „-2 GB - 10 bajtów”, wskazujący obszar bezpośrednio poprzedzający przydzielony bufor.

Przygotowany exploit wymaga do działania 5 GB pamięci i 1 miliona wolnych i-węzłów. Exploit działa poprzez wywołanie mkdir() w celu utworzenia hierarchii składającej się z około miliona podkatalogów, w celu osiągnięcia rozmiaru ścieżki pliku przekraczającego 1 GB. Katalog ten jest montowany za pomocą bind-mount w osobnej przestrzeni nazw użytkownika, po czym uruchamiana jest funkcja rmdir() w celu jego usunięcia. Równolegle tworzony jest wątek ładujący mały program eBPF, który jest blokowany na etapie po sprawdzeniu pseudokodu eBPF, ale przed jego kompilacją JIT.

W nieuprzywilejowanej przestrzeni nazw użytkownika otwierany jest plik /proc/self/mountinfo i odczytywana jest długa ścieżka katalogu zamontowanego przez powiązanie, w wyniku czego ciąg znaków „//deleted” jest zapisywany w obszarze przed uruchomieniem bufora. Pozycja zapisu linii jest tak dobrana, aby nadpisywała instrukcję w już przetestowanym, ale jeszcze nie skompilowanym programie eBPF.

Następnie, na poziomie programu eBPF, niekontrolowany zapis poza buforem jest przekształcany w kontrolowaną możliwość odczytu i zapisu do innych struktur jądra poprzez manipulację strukturami btf i map_push_elem. W rezultacie exploit określa lokalizację bufora modprobe_path[] w pamięci jądra i nadpisuje w nim znajdującą się ścieżkę „/sbin/modprobe”, co umożliwia zainicjowanie uruchomienia dowolnego pliku wykonywalnego z uprawnieniami roota w przypadku request_module(), które jest wykonywane na przykład podczas tworzenia gniazda netlink.

Badacze podają kilka obejść, które są skuteczne tylko w przypadku konkretnego exploita, ale nie eliminują samego problemu. Zalecane jest ustawienie "/proc/sys/kernel/unprivileged_userns_clone" na 0, aby wyłączyć montowanie katalogów w oddzielnej przestrzeni nazw identyfikatora użytkownika, i "/proc/sys/kernel/unprivileged_bpf_disabled" na 1, aby wyłączyć ładowanie programów eBPF do jądra.

Warto zauważyć, że analizując alternatywny atak polegający na wykorzystaniu mechanizmu FUSE zamiast bind-mound w celu zamontowania dużego katalogu, badacze natknęli się na kolejną lukę (CVE-2021-33910) wpływającą na menedżera systemu systemd. Okazało się, że podczas próby zamontowania katalogu o rozmiarze ścieżki przekraczającym 8 MB za pomocą FUSE, procesowi inicjalizacji sterowania (PID1) kończy się pamięć stosu i ulega awarii, co wprowadza system w stan „paniki”.

Problem polega na tym, że systemd śledzi i analizuje zawartość /proc/self/mountinfo i przetwarza każdy punkt podłączenia w funkcji nazwa_jednostki_path_escape(), która wykonuje operację strdupa() umieszczającą dane na stosie, a nie w dynamicznie alokowanej pamięci . Ponieważ maksymalny rozmiar stosu jest ograniczony przez RLIMIT_STACK, przetwarzanie zbyt dużej ścieżki do punktu podłączenia powoduje awarię procesu PID1 i zatrzymanie systemu. Do ataku można użyć najprostszego modułu FUSE w połączeniu z wykorzystaniem wysoce zagnieżdżonego katalogu jako punktu montowania, którego rozmiar ścieżki przekracza 8 MB.

Problem pojawia się od wersji systemd 220 (kwiecień 2015), został już naprawiony w głównym repozytorium systemd i naprawiony w dystrybucjach (Debian, Ubuntu, Fedora, RHEL, SUSE, Arch). Warto zauważyć, że w wersji systemd 248 exploit nie działa z powodu błędu w kodzie systemd, który powoduje niepowodzenie przetwarzania /proc/self/mountinfo. Co ciekawe, w 2018 roku miała miejsce podobna sytuacja i próbując napisać exploita wykorzystującego lukę CVE-2018-14634 w jądrze Linuksa, badacze Qualys natknęli się na trzy krytyczne luki w systemd.

Źródło: opennet.ru

Dodaj komentarz