Google Project Zero odkrył, jak ominąć funkcję Hypervisor Knox firmy Samsung (naprawiono w styczniowej łatce)

W najnowszym poście na blogu Project Zero zespół odkrył sposób na ominięcie działającej w czasie rzeczywistym ochrony jądra firmy Samsung, zwanej Knox Hypervisor.

Zespół Google Project Zero zweryfikował szereg exploitów, które umożliwiają atak na telefony Samsung z rzekomo bezpiecznym pakietem zabezpieczającym Samsung Knox. Na blogu zauważono, że wszystkie luki zostały przekazane firmie Samsung, która faktycznie wydała dla nich poprawki w styczniowej aktualizacji oprogramowania.


Tło

Jako część pakietu oprogramowania zabezpieczającego Samsung Knox wprowadzonego przez firmę Samsung, pomiędzy aplikacjami systemu Android a jądrem znajduje się oprogramowanie zwane Hiperwizor. Można to wykorzystać jako dodatkową warstwę w celu dalszego zabezpieczenia urządzeń z Androidem. Hypervisor Samsung Knox nazywa się „Ochrona jądra w czasie rzeczywistym” lub w skrócie RKP, o czym będę wspominać w dalszej części tego artykułu.

Jądro znajduje się poniżej RKP w stosie oprogramowania Androida, a aplikacje działające na urządzeniu znajdują się na górze. Ideą RKP jest zapewnienie dodatkowej warstwy bezpieczeństwa dla urządzenia, ponieważ wszystkie żądania (pamięć i inne zasoby) wysyłane przez aplikacje do jądra muszą najpierw przejść przez Knox, który próbuje wykryć, czy aplikacja coś robi nie powinien. RKP zapewnia również bezpieczeństwo poprzez zaciemnienie dzięki dodatkowej warstwie ukrywającej poufne informacje, które aplikacja może wykorzystać do złamania zabezpieczeń urządzenia.

Wpis na blogu dość szczegółowo omawia działanie pamięci Androida, RKP i systemów operacyjnych w ogóle, więc skondensowałem go i uprościłem, aby dać szybki przegląd tego, co odkryto. Zachęcam Cię jednak do przeczytania całego artykułu, jeśli masz czas, ponieważ jest on bardzo pouczający.


Exploit nr 1:

KASLR lub Randomizacja układu przestrzeni adresowej jądra to proces zmiany lokalizacji kodu jądra w pamięci o losową wartość podczas rozruchu. Za każdym razem, gdy urządzenie jest uruchamiane, jądro jest ładowane do innej przestrzeni adresowej (obszaru pamięci). Pomysł jest taki, aby utrudnić znalezienie lokalizacji kodu jądra w celu zaatakowania go, ponieważ po każdym uruchomieniu kod jądra „przesuwa się” o losową ilość w pamięci. Brzmi to jak świetny krok w zapobieganiu potencjalnym atakującym, ale niedawnym badania pokazało, że faktycznie można to pokonać bez konieczności stosowania błędu oprogramowania lub luki w zabezpieczeniach, ponieważ KASLR jest w rzeczywistości bardzo trudny do skutecznego wdrożenia przeciwko lokalnym atakującym.

W przypadku oprogramowania RKP możliwość ominięcia KASLR jest w rzeczywistości prostsza niż przywołane powyżej badania. Do pamięci wszystkich urządzeń z systemem Android odwołują się wskaźniki, co ma na celu ochronę urządzeń przed atakiem za każdym razem, gdy urządzenie z systemem Android drukuje lub wysyła dane wyjściowe (czy to na ekran, czy do pliku dziennika lub debugowania), odniesienia do wskaźników są anonimowe, - co uniemożliwia ustalenie, gdzie wskaźnik faktycznie wskazuje podczas czytania wyjście.

Pomyśl o wskaźnikach pamięci jak o znaku drogowym wskazującym lokalizację i pomyśl o anonimizacji jako o zacieraniu tego. Podobnie jak w telewizji, anonimizacja odbywa się po filmowaniu. Android stosuje tę anonimizację również w momencie wyjścia i tylko wtedy, gdy anonimizacja jest poprawnie skonfigurowana, a autor stwierdza że każde napotkane urządzenie ma poprawnie skonfigurowaną anonimizację wskaźników. Może się to wydawać trudne do złamania, ale wszystko, co musisz zrobić, to znaleźć pojedynczy wskaźnik (pomyśl o znaku drogowym), który nie został zanonimizowany (zamazany) przez twórcę jądra (uważaj, że nie jest to przeciętny twórca aplikacji na Androida), gdy wskaźnik jest zapisywany w dziennikach lub w innym miejscu, np. ekran lub plik.

Jeśli więc znajdziesz wskaźnik, który nie został zanonimizowany, możesz obliczyć losowe przesunięcie adresu jądra jako różnicę między nimi. Co ciekawe, autor nie mógł znaleźć nadającego się do wykorzystania wskaźnika w jądrze, ale znalazł go w RPK gdzie programiści zapomnieli zanonimizować wskaźnik w wynikach debugowania (logowania), co nastąpiło w drodze a literówka. Aby zanonimizować wskaźniki w Androidzie trzeba użyć specjalnego kodu i okazuje się, że twórcy RPK omyłkowo użyli mała litera „k” zamiast wielkie „K”. Dlatego stosunkowo łatwo było ustalić wielkość losowego przesunięcia kodu jądra i zaatakować go.


Exploit nr 2:

Następny exploit jest nieco bardziej złożony: Samsung Knox chroni Twoje urządzenie, stosując zestaw reguł do pamięci urządzenia, aby zatrzymać złośliwy kod. Zasady są następujące:

  1. Wszystkie strony (kod w pamięci), z wyjątkiem kodu jądra, są oznaczone jako „Uprzywilejowane wykonywanie nigdy” (co oznacza, że ​​kod tutaj nigdy nie może zostać uruchomiony)
  2. Strony danych jądra (dane używane przez program w pamięci) nigdy nie są oznaczone jako wykonywalne (więc kod tutaj nigdy nie może zostać uruchomiony)
  3. Strony kodowe jądra (kod w pamięci) nigdy nie są oznaczone jako nadające się do zapisu (więc żaden złośliwy kod nie może ich zmienić)
  4. Wszystkie strony jądra są oznaczone jako tylko do odczytu w tabeli tłumaczeń etapu 2 (tabela znajdująca się pomiędzy aplikacją a jądrem, aby jeszcze bardziej uniemożliwić aplikacjom poznanie rzeczywistych lokalizacji pamięci)
  5. Wszystkie wpisy translacji pamięci są oznaczone jako tylko do odczytu dla aplikacji.

Skupimy się na regule 3, gdyż to tutaj autor znalazł problem z implementacją powyższych zasad. RPK faktycznie oznacza pamięć jądra jako tylko do odczytu, jednak w wyniku niedopatrzenia KASL odkryto lukę, która doprowadziła do zapisywanie kodu w rzekomo „tylko do odczytu” sekcji. Aby zaciemnić lokalizację jądra podczas rozruchu, pamięć jest przydzielana do jądra, ale ta ilość pamięci jest znacznie większa niż segment tekstowy jądra. Przydzielenie większej ilości pamięci znacznie utrudnia znalezienie rzeczywistego kodu jądra, który może znajdować się gdziekolwiek, a jak widzieliśmy powyżej, jest on losowo przenoszony przy każdym uruchomieniu urządzenia.

_text i _etext oznaczają chroniony zakres

Autorowi udało się potwierdzić, że pamięć używana przez jądro rzeczywiście była oznaczona jako „tylko do odczytu”, jednak reszta tej dużej ilości pamięci używanej do ukrywania jądra była nie oznaczony jako „tylko do odczytu”. Dzieje się tak dlatego, że RKP chroni jedynie region zawierający tekst jądra po zastosowaniu slajdu KASLR.


Exploit nr 3

W trzecim exploitu autorowi udało się uzyskać dostęp do innego obszaru pamięci, który również powinien być ograniczony do trybu tylko do odczytu. RKP chroni pamięć i wykorzystuje a Rejestr konfiguracji hypervisora (HCR) do kontrolowania kluczowych operacji jądra. Celem HCR jest umożliwienie prawidłowym i rzeczywistym operacjom jądra dostępu do rejestrów i zablokowanie złośliwych ataków. Robi to poprzez sprawdzanie wywołań rejestrów zarządzających funkcjami wirtualizacji. HCR jest skonfigurowany tak, aby blokować określone operacje, które byłyby obsługiwane normalnie, umożliwiając RKP wybór, czy zezwolić na żądanie, czy nie.

W tym exploitu kontrola HCR była nieobejmującego dwóch rejestrów okazało się to bardzo ważne. Autor pogrzebał głęboko w podręczniku ARM Reference i odkrył, że pierwszy rejestr pozwolił mu w zasadzie wyłączyć RKP dla aplikacji. „Rejestr kontroli systemu dla EL1 (SCTLR_EL1) zapewnia kontrolę najwyższego poziomu nad systemem, w tym nad systemem pamięci.” W idealnym świecie aplikacja korzystałaby z pamięci mapowanej przez RKP, dzięki czemu RKP mógłby kontrolować, do czego aplikacja może uzyskać dostęp. Jednakże wyłączenie tego rejestru umożliwiło RKP do wyłączenia poprzez skuteczne przywrócenie urządzenia do stanu sprzed zainstalowania RKP – co oznacza, że ​​urządzenie jest mapowane do pamięci fizycznej bez dodatkowych zabezpieczeń zapewnianych przez RKP. To z kolei oznaczało, że autor mógł czytać i zapisywać w pamięci, która oryginalnie i poprawnie została zablokowana przez oprogramowanie RKP.

Drugi rejestr, który został pominięty, miał bardziej subtelny wpływ, ale ostatecznie był równie niszczycielski dla bezpieczeństwa. The Rejestr kontroli translacji dla EL1 Rejestr (TCR_EL1) bezpośrednio odnosi się do ilości pamięci, z jaką współpracuje aplikacja zwana stroną. RKP jest zakodowany na stałe do rozmiaru strony 4 KB, ponieważ jądra Linuksa AARCH64 (takie jak Android) używają rozmiaru tłumaczenia 4 KB. Rejestr, o którym mowa (TCR_EL1), ustawia chipsety ARM na wielkość pamięci, która ma zostać zwrócona. Okazało się, że rejestr ten nie jest chroniony przez HCR dlatego osoba atakująca może go zmienić, tak jak autor zmienił go na rozmiar strony 64 KB.

Oznacza to, że gdy żądanie zostanie spełnione przez RKP, rzeczywista ilość dostępnej pamięci wynosi teraz 64 KB zamiast 4 KB. Powodem jest to, że chipset ARM nadal kontroluje rozmiar strony, a exploit ustawił go na 64 KB. Ponieważ RKP chroni pamięć przed zapisem, w ramach zasad wymienionych w exploitu nr 2, pamięć jest nadal faktycznie chroniona. Ale tutaj jest haczyk - ponieważ RKP jest zakodowany na stałe na 4kb, nie zmienia się na rozmiar strony 64kb po aktualizacji rejestru, więc chronione są tylko pierwsze 4 KB pamięci pozwalając atakującemu to zrobić co chce z pozostałymi 60 KB.


Exploit nr 4

Ostatni exploit pokazany przez autora polega na odwoływaniu się do pamięci, w której znajduje się oprogramowanie RKP, więc osoba atakująca może zaatakować samo oprogramowanie RKP. Jedną ze sztuczek pozwalających powstrzymać tego typu ataki, z których korzystają również jądra Linuksa, jest odmapowanie programu z przestrzeni adresowej pamięci wirtualnej, tak aby żadna aplikacja nie mogła go zaatakować, ponieważ nie może się do niego odwołać.

Pamiętaj, że pamięć składa się ze wskaźników i tabel, które odwzorowują pamięć fizyczną na pamięć wirtualną. Zgodnie z normalną obroną w przypadku tego typu ataku, RKP usuwa mapowanie, dzięki czemu nie można go zaatakować. Jednakże, jeśli jądro nie zapewnia takich możliwości, RKP pozwala na zmapowanie fragmentu pamięci i oznaczenie go jako Odczyt/Zapis. Jedynym sprawdzeniem jest to, czy nie jest to samo jądro, ponieważ RKP nie sprawdza, czy adresy żądane do mapowania to obszar, w którym sam RKP znajduje się w pamięci. W zasadzie RKP pozwala na ponowne mapowanie z powrotem do przestrzeni adresowej, do której aplikacje mogą uzyskać dostęp i jako strona wpływają na pamięć jest automatycznie oznaczona jako Odczyt/Zapis więc osoba atakująca może teraz korzystać z pamięci w dowolny sposób.


Wniosek

Jednym z największych problemów z czterema wymienionymi powyżej exploitami jest to, że autor wspomina, jak trudne byłoby ich wykonanie ze względu na brak funkcji w podstawowym jądrze Androida. Jak na ironię, bezpieczny Hypervisor RKP zapewnił wszystkie narzędzia wymagane do przeprowadzenia ataków. To pokazuje, że czasami oprogramowanie powstające w dobrych intencjach powoduje więcej problemów, niż rozwiązuje, i mamy szczęście, że mamy ludzi, którzy jak Gal Beniamini, który chce ubrudzić sobie ręce i sprawdzić, czy dokumentacja odpowiada faktycznemu oprogramowaniu robi.

Chociaż te exploity wydają się przerażające i sprawiają, że Knox wydaje się bardzo bezbronny, chciałbym wszystkich zapewnić, że wszystkie te problemy zostały rozwiązane naprawiono w styczniowej aktualizacji od Samsunga. Co więcej, exploity te wymagają bardzo głębokiej wiedzy na temat procesorów ARM i programowania, dlatego bariera wejścia na rynek przy użyciu tych exploitów jest astronomicznie wysoka.


Źródło: Projekt Zero