iOS Exploiting
Fizyczne użycie po zwolnieniu
To jest podsumowanie z posta z https://alfiecg.uk/2024/09/24/Kernel-exploit.html, ponadto dalsze informacje na temat exploita wykorzystującego tę technikę można znaleźć w https://github.com/felix-pb/kfd
Zarządzanie pamięcią w XNU
Wirtualna przestrzeń adresowa pamięci dla procesów użytkownika na iOS rozciąga się od 0x0 do 0x8000000000. Jednak te adresy nie mapują się bezpośrednio na pamięć fizyczną. Zamiast tego, jądro używa tabel stron do tłumaczenia adresów wirtualnych na rzeczywiste adresy fizyczne.
Poziomy tabel stron w iOS
Tabele stron są zorganizowane hierarchicznie w trzech poziomach:
Tabela stron L1 (Poziom 1):
Każdy wpis tutaj reprezentuje duży zakres pamięci wirtualnej.
Pokrywa 0x1000000000 bajtów (lub 256 GB) pamięci wirtualnej.
Tabela stron L2 (Poziom 2):
Wpis tutaj reprezentuje mniejszy obszar pamięci wirtualnej, konkretnie 0x2000000 bajtów (32 MB).
Wpis L1 może wskazywać na tabelę L2, jeśli nie może samodzielnie zmapować całego obszaru.
Tabela stron L3 (Poziom 3):
To najdrobniejszy poziom, gdzie każdy wpis mapuje pojedynczą stronę pamięci 4 KB.
Wpis L2 może wskazywać na tabelę L3, jeśli potrzebna jest bardziej szczegółowa kontrola.
Mapowanie pamięci wirtualnej na fizyczną
Bezpośrednie mapowanie (Mapowanie blokowe):
Niektóre wpisy w tabeli stron bezpośrednio mapują zakres adresów wirtualnych na ciągły zakres adresów fizycznych (jak skrót).
Wskaźnik do tabeli stron podrzędnych:
Jeśli potrzebna jest dokładniejsza kontrola, wpis na jednym poziomie (np. L1) może wskazywać na tabelę stron podrzędnych na następnym poziomie (np. L2).
Przykład: Mapowanie adresu wirtualnego
Załóżmy, że próbujesz uzyskać dostęp do adresu wirtualnego 0x1000000000:
Tabela L1:
Jądro sprawdza wpis w tabeli stron L1 odpowiadający temu adresowi wirtualnemu. Jeśli ma wskaźnik do tabeli stron L2, przechodzi do tej tabeli L2.
Tabela L2:
Jądro sprawdza tabelę stron L2 w poszukiwaniu bardziej szczegółowego mapowania. Jeśli ten wpis wskazuje na tabelę stron L3, przechodzi tam.
Tabela L3:
Jądro przeszukuje końcowy wpis L3, który wskazuje na adres fizyczny rzeczywistej strony pamięci.
Przykład mapowania adresów
Jeśli zapiszesz adres fizyczny 0x800004000 w pierwszym indeksie tabeli L2, to:
Adresy wirtualne od 0x1000000000 do 0x1002000000 mapują się na adresy fizyczne od 0x800004000 do 0x802004000.
To jest mapowanie blokowe na poziomie L2.
Alternatywnie, jeśli wpis L2 wskazuje na tabelę L3:
Każda strona 4 KB w zakresie adresów wirtualnych 0x1000000000 -> 0x1002000000 byłaby mapowana przez indywidualne wpisy w tabeli L3.
Fizyczne użycie po zwolnieniu
Fizyczne użycie po zwolnieniu (UAF) występuje, gdy:
Proces alokuje pewną pamięć jako czytelną i zapisywalną.
Tabele stron są aktualizowane, aby mapować tę pamięć do konkretnego adresu fizycznego, do którego proces ma dostęp.
Proces zwalnia (uwalnia) pamięć.
Jednak z powodu błędu, jądro zapomina usunąć mapowanie z tabel stron, mimo że oznacza odpowiadającą pamięć fizyczną jako wolną.
Jądro może następnie ponownie przydzielić tę "zwolnioną" pamięć fizyczną do innych celów, takich jak dane jądra.
Ponieważ mapowanie nie zostało usunięte, proces może nadal czytać i pisać do tej pamięci fizycznej.
Oznacza to, że proces może uzyskać dostęp do stron pamięci jądra, które mogą zawierać wrażliwe dane lub struktury, co potencjalnie pozwala atakującemu na manipulację pamięcią jądra.
Strategia eksploatacji: Spray na stercie
Ponieważ atakujący nie może kontrolować, które konkretne strony jądra będą przydzielane do zwolnionej pamięci, używają techniki zwanej spray na stercie:
Atakujący tworzy dużą liczbę obiektów IOSurface w pamięci jądra.
Każdy obiekt IOSurface zawiera magiczna wartość w jednym ze swoich pól, co ułatwia identyfikację.
Skanują zwolnione strony, aby sprawdzić, czy którykolwiek z tych obiektów IOSurface wylądował na zwolnionej stronie.
Gdy znajdą obiekt IOSurface na zwolnionej stronie, mogą go użyć do czytania i pisania pamięci jądra.
Więcej informacji na ten temat w https://github.com/felix-pb/kfd/tree/main/writeups
Proces sprayowania na stercie krok po kroku
Spray obiektów IOSurface: Atakujący tworzy wiele obiektów IOSurface z specjalnym identyfikatorem ("magiczna wartość").
Skanowanie zwolnionych stron: Sprawdzają, czy którykolwiek z obiektów został przydzielony na zwolnionej stronie.
Czytanie/Pisanie pamięci jądra: Manipulując polami w obiekcie IOSurface, uzyskują możliwość wykonywania dowolnych odczytów i zapisów w pamięci jądra. To pozwala im:
Użyć jednego pola do czytania dowolnej wartości 32-bitowej w pamięci jądra.
Użyć innego pola do zapisywania wartości 64-bitowych, osiągając stabilny prymityw odczytu/zapisu jądra.
Generuj obiekty IOSurface z magiczną wartością IOSURFACE_MAGIC, aby później je wyszukiwać:
Szukaj obiektów IOSurface
w jednej zwolnionej stronie fizycznej:
Osiąganie odczytu/zapisu jądra z IOSurface
Po uzyskaniu kontroli nad obiektem IOSurface w pamięci jądra (mapowanym na zwolnioną stronę fizyczną dostępną z przestrzeni użytkownika), możemy go użyć do dowolnych operacji odczytu i zapisu w jądrze.
Kluczowe pola w IOSurface
Obiekt IOSurface ma dwa kluczowe pola:
Wskaźnik liczby użyć: Umożliwia odczyt 32-bitowy.
Wskaźnik znaczników czasowych: Umożliwia zapis 64-bitowy.
Przez nadpisanie tych wskaźników, przekierowujemy je do dowolnych adresów w pamięci jądra, co umożliwia operacje odczytu/zapisu.
Odczyt jądra 32-bitowy
Aby wykonać odczyt:
Nadpisz wskaźnik liczby użyć, aby wskazywał na docelowy adres minus offset 0x14 bajtów.
Użyj metody
get_use_count
, aby odczytać wartość pod tym adresem.
64-Bit Kernel Write
Aby wykonać zapis:
Nadpisz wskaźnik znaczników indeksowanych na docelowy adres.
Użyj metody
set_indexed_timestamp
, aby zapisać 64-bitową wartość.
Podsumowanie Przepływu Eksploatacji
Wywołaj Fizyczne Użycie-Po-Zwolnieniu: Zwolnione strony są dostępne do ponownego użycia.
Spray Obiektów IOSurface: Przydziel wiele obiektów IOSurface z unikalną "magiczna wartością" w pamięci jądra.
Zidentyfikuj Dostępny IOSurface: Zlokalizuj IOSurface na zwolnionej stronie, którą kontrolujesz.
Wykorzystaj Użycie-Po-Zwolnieniu: Zmodyfikuj wskaźniki w obiekcie IOSurface, aby umożliwić dowolne odczyty/zapisy jądra za pomocą metod IOSurface.
Dzięki tym prymitywom, eksploatacja zapewnia kontrolowane odczyty 32-bitowe i zapisy 64-bitowe do pamięci jądra. Dalsze kroki jailbreak mogą obejmować bardziej stabilne prymitywy odczytu/zapisu, które mogą wymagać ominięcia dodatkowych zabezpieczeń (np. PPL na nowszych urządzeniach arm64e).
Last updated