Inżynieria wsteczna sterownika

https://chacker.pl/

Mając plik sterownika w ręku, załaduj go do wybranego deasemblera i zbadaj punkt wejścia — w przykładach użyto IDA Pro.

UWAGA: Proces inżynierii wstecznej w tym laboratorium ma na celu wskazanie odpowiednich części programu. Być może będziesz musiał poświęcić czas na zapoznanie się z dokumentacją i inżynierią wsteczną, aby dojść do tych samych wniosków!

Wszystkie sterowniki zaczynają się od funkcji DriverEntry. W zależności od ustawień kompilatora, funkcja DriverEntry będzie albo tym, co napisał programista, albo automatycznie wstawionym stubem, który inicjuje plik cookie bezpieczeństwa dla całego sterownika, a następnie przechodzi do oryginalnego DriverEntry. Ten sterownik w rzeczywistości ma automatycznie wstawiony stub znany jako GsDriverEntry. Znajdź ostatnią instrukcję tej funkcji (jmp) i przejdź do funkcji, do której się odwołuje; ta funkcja jest prawdziwym DriverEntry. Na górze prawdziwego DriverEntry powinieneś zobaczyć kilka wywołań memmove i RtlInitUnicodeString, jak pokazano poniżej. Twój deasembler może lub nie pokazywać ciągów znaków, do których się odwołujesz.

Pokazane ciągi są ważne, ponieważ są następnie przekazywane do IoCreateDevice i IoCreateSymbolicLink. Oznacza to, że będziemy mogli wchodzić w interakcję z utworzonym urządzeniem z trybu użytkownika za pośrednictwem symlinku. Wywołanie IoCreateDevice pokazuje kilka innych informacji, takich jak DeviceType (0x9B0C) i DeviceExtensionSize (0xA0), jak pokazano poniżej.

Jeśli zarówno urządzenie, jak i utworzenie dowiązania symbolicznego powiedzie się, sterownik przeniesie wskaźnik funkcji do rax, a następnie wskaźnik funkcji zostanie przeniesiony do różnych przesunięć od rdi, jak pokazano na poniższej ilustracji. Prześledź, co znajduje się w rdi, a powinieneś odkryć, że jest to wskaźnik do _DRIVER_OBJECT, pierwotnie w rcx. Zaczynając od przesunięcia 0x70 w _DRIVER_OBJECT znajduje się tablica MajorFunction, więc funkcja przenoszona do rax musi być głównym programem obsługi funkcji dla sterownika. Obsługuje on cztery główne funkcje:

IRP_MJ_CREATE (0), IRP_MJ_CLOSE (2),

IRP_MJ_DEVICE_CONTROL (14) i

IRP_MJ_INTERNAL_DEVICE_CONTROL (15).

Następnie spójrz na górę głównego handlera funkcji, pokazanego tutaj. Dla ułatwienia zrozumienia, niektóre instrukcje zostały opatrzone adnotacjami o przesunięciach struktury i odpowiednimi wartościami stałych.

Jak widać, funkcja odwołuje się do pól w obu argumentach przekazanych do głównego programu obsługi funkcji: _DEVICE_OBJECT w rcx i _IRP w rdx. Pamiętaj, że struktura _IRP zawiera szczegóły dotyczące wykonywanego żądania. Najpierw _IO_STACK_LOCATION jest przenoszone do r8, a DeviceExtension jest przenoszone do rdi. Następnie stała 14 (IRP_MJ_DEVICE_CONTROL) jest porównywana z pierwszym bajtem _IO_STACK_LOCATION, który jest polem MajorFunction. Gdy sterowanie urządzeniem I/O jest przekazywane do sterownika, to sprawdzenie nie wykona skoku, zamiast tego kontynuując do następnego bloku. W następnym bloku bufor wejściowy (_IRP->AssociatedIrp.SystemBuffer) jest przenoszony do rax, a następnie umieszczany w rdi+0, czyli DeviceExtension+0. Następnie długość bufora wejściowego (_IO_STACK_LOCATION->Parameters.DeviceIoControl.InputBufferLength) jest przenoszona do DeviceExtension+8. Zobaczymy te dwie wartości odwoływane później, więc miej je na uwadze. Następnie długość bufora wejściowego jest porównywana z długością bufora wyjściowego (_IO_STACK_LOCATION->Parameters.DeviceIoControl.OutputBufferLength) i nie kontynuuje przetwarzania żądania sterowania I/O, jeśli nie są równe. Ta informacja stanie się ważna, gdy później spróbujemy napisać kod do interakcji ze sterownikiem. Podczas wyszukiwania luk w skompilowanych programach dobrą praktyką jest rozpoczęcie od wyszukiwania wywołań funkcji, które manipulują pamięcią, takich jak strcpy, memcpy i memmove. Otwórz odwołania krzyżowe do funkcji memmove w swoim deasemblerze. W IDA naciśnij klawisz X na funkcji, aby wyświetlić okno pokazane obok.

Poświęć trochę czasu na przejrzenie wszystkich tych wywołań memmove. Prześledź ich argumenty (rcx, rdx i r8), aby sprawdzić, czy możesz kontrolować którykolwiek z nich. Pamiętaj, że wartości pobrane ze struktur _IRP->AssociatedIrp.SystemBuffer i _IO_STACK_LOCATION->Parameters.DeviceIoControl można bezpośrednio kontrolować z trybu użytkownika. Pamiętaj również, że SystemBuffer i InputBufferSize zostały przeniesione do DeviceExtension odpowiednio na przesunięciach 0 i 8. Mam nadzieję, że po pewnych poszukiwaniach uznasz wywołanie memmove w sub_15294 za interesujące.

W zależności od wartości dl, wartość w r9+0x18 jest przenoszona do rcx (miejsce docelowe) lub rdx (źródło). Na początku tego fragmentu kodu, inny argument memmove znajduje się w rcx, a rozmiar ruchu jest przenoszony z eax do r8d. Prześledź dalej, aby zobaczyć, skąd pochodzą r9, rcx i eax, jak pokazano poniżej.

Wygląda na to, że rax pochodzi z rbx+0x10, a r9 pochodzi z rbx. W tym momencie wiemy, że zarówno argument rozmiaru, jak i źródło lub miejsce docelowe pochodzą z bufora w rbx. Kontynuuj śledzenie, aby znaleźć, że rcx (pierwszy argument) został przeniesiony do rbx w pierwszym bloku funkcji. Śledzenie do wywołującego za pomocą odniesienia krzyżowego pokazuje, że rdi został przeniesiony do rcx wewnątrz głównego programu obsługi funkcji, jak pokazano tutaj.

Wygląda na to, że rax pochodzi z rbx+0x10, a r9 pochodzi z rbx. W tym momencie wiemy, że zarówno argument rozmiaru, jak i źródło lub miejsce docelowe pochodzą z bufora w rbx. Kontynuuj śledzenie, aby znaleźć, że rcx (pierwszy argument) został przeniesiony do rbx w pierwszym bloku funkcji. Śledzenie do wywołującego za pomocą odniesienia krzyżowego pokazuje, że rdi został przeniesiony do rcx wewnątrz głównego programu obsługi funkcji, jak pokazano tutaj.

Prześledzenie jednego bloku z każdego z tych dwóch bloków ujawnia kody IOCTL dla każdego z nich: 0x9B0C1EC4 i 0x9B0C1EC8.

W tym momencie mamy wszystkie informacje, których potrzebujemy, aby przejść do dynamicznej analizy sterownika. Jako dodatkowe ćwiczenie spróbuj dowiedzieć się, co robią inne IOCTL w tym sterowniku. Funkcjonalność, którą właśnie zidentyfikowaliśmy, nie jest jedynym problemem z tym sterownikiem!

Uzyskiwanie sterownika docelowego

https://chacker.pl/

W ostrzeżeniu firmy Dell napisano, że luki w zabezpieczeniach dotyczą „pakietów narzędzi do aktualizacji oprogramowania układowego, w tym narzędzi do aktualizacji BIOS-u, narzędzi do aktualizacji oprogramowania układowego Thunderbolt, narzędzi do aktualizacji oprogramowania układowego TPM i narzędzi do aktualizacji oprogramowania układowego stacji dokującej”.4 Mając to na uwadze, przejdź do witryny firmy Dell i zacznij szukać potencjalnie zagrożonych aktualizacji. Jednym z aktualizatorów, który zawiera sterownik, jest aktualizacja BIOS-u Dell Latitude 7204 Rugged A16. W chwili pisania tego tekstu ta aktualizacja jest najnowszą dla tego systemu i nadal zapisuje podatny sterownik na dysku. Jako dodatkowe ćwiczenie spróbuj znaleźć inną aktualizację, która zawiera podatny sterownik. Jeśli znajdziesz inne sterowniki po drodze, zapisz je do późniejszego ćwiczenia inżynierii wstecznej. Wspomniany aktualizator i kopię sterownika docelowego można znaleźć w repozytorium GitHub książki. Uruchom aktualizator BIOS-u (lub wybraną aktualizację) w systemie Windows i sprawdź w C:\Users\<Twój użytkownik>\AppData\Local\Temp plik o nazwie DBUtil_2_3.sys. Jeśli nie możesz znaleźć pliku, poszukaj w C:\Windows\Temp. Możesz również uruchomić Sysinternals Process Monitor i ustawić filtr dla „Path, Ends With, DBUtil_2_3.sys”, aby zobaczyć, kiedy sterownik jest zapisywany na dysku lub wywoływany.

Wybór celu

https://chacker.pl/

Jednym z najbardziej palących i istotnych pytań w badaniach nad lukami w zabezpieczeniach jest „jak wybrać cel?”. Chociaż możemy nie być w stanie odpowiedzieć na to pytanie, warto się nad nim zastanowić, ponieważ odnosi się ono do bieżącego tematu. Jeśli chcesz wejść w eksploatację jądra systemu Windows i sterowników jądra, od czego zacząć? Rozpoczęcie od próby znalezienia luk w samym jądrze lub sterownikach opracowanych przez Microsoft może być nieco trudne lub zniechęcające. Jednym z łatwiejszych i bardziej dostępnych punktów wyjścia są znane podatne sterowniki. Kiedyś Microsoft miał znacznie mniej rygorystyczny proces podpisywania sterownika. Obecnie Microsoft wymaga od programistów sterowników przesyłania ich sterowników do portalu w celu uzyskania podpisu wydania Windows Hardware Quality Labs (WHQL).2 Microsoft wydawał certyfikaty wydawcy oprogramowania (SPC), aby strony trzecie mogły podpisywać własne sterowniki przed publikacją; zaprzestali programu po wycieku kilku certyfikatów, a niektórzy wydawcy podpisywali źle zaprojektowane lub celowo niedostatecznie zabezpieczone sterowniki. Niektóre z tych podpisanych sterowników SPC są nadal szeroko dystrybuowane, jak zobaczysz w tej sekcji.

W sierpniu 2019 r. na konferencji DEFCON 27 badacze z Eclypsium Labs zaprezentowali szereg sterowników z lukami, podkreślając ten konkretny problem. Na ich liście znajdowało się 39 sterowników, które umożliwiają operacje takie jak dowolny wirtualny i fizyczny odczyt i zapis, dowolny odczyt-zapis-wykonanie alokacji pamięci jądra oraz dowolny odczyt i zapis rejestru specyficznego dla modelu (MSR). Funkcje te nie są z natury lukami, ponieważ uprzywilejowane aplikacje, takie jak aktualizatory BIOS-u, muszą ich używać, aby działać prawidłowo, ale tutaj liczy się dostęp wymagany do ich wykorzystania. Te sterowniki są dostępne z trybu użytkownika na dowolnym poziomie uprawnień w systemie. W niektórych przypadkach nawet procesy działające z niską lub niezaufaną integralnością mogą je wywołać. Oznacza to, że każdy z wykonywaniem kodu może potencjalnie podnieść swoje uprawnienia do SYSTEMU lub jądra. Sterowniki utworzone przy użyciu starszego modelu sterowników systemu Windows (WDM) mają domyślnie otwarte uprawnienia dostępu. Listy kontroli dostępu można ustawić za pośrednictwem interfejsu API systemu Windows lub we wpisie rejestru dla sterownika; jednak twórcy tych sterowników nie zrobili ani jednego, ani drugiego, ujawniając w ten sposób uprzywilejowaną funkcjonalność. W maju 2021 r. badacz Sentinel Labs Kasif Dekel opublikował artykuł szczegółowo opisujący szeroko rozpowszechniony sterownik Dell z podobnymi problemami do tych na liście sterowników Eclypsium.4 Jedną z interesujących rzeczy dotyczących tego sterownika jest zakres dystrybucji — prawie 400 platform zostało dotkniętych tym problemem i ujawnieniem. Sterownik nazywa się DBUtil_2_3.sys i jest dołączony do narzędzi aktualizujących Dell i Alienware od 2009 r. Został podpisany przez zewnętrzną firmę Dell SPC i nie został sprawdzony ani przesłany do firmy Microsoft. Ponieważ jest to niedawna luka w zabezpieczeniach i ma tak duży zakres, jest idealnym celem do nauki wykorzystywania jądra.

Konfigurowanie debugowania jądra

https://chacker.pl/

Aby rozpocząć, będziesz potrzebować dwóch maszyn wirtualnych z systemem Windows 10 i wybranego oprogramowania do wirtualizacji (VMware, VirtualBox, Parallels itd.). Możesz również użyć systemu Windows 11, jeśli masz jego kopię, ponieważ proces i wyniki powinny być takie same. Jeśli masz licencję systemu Windows i maszyny wirtualne są już skonfigurowane, to świetnie! Jeśli w ogóle nie masz maszyn wirtualnych z systemem Windows 10, masz kilka opcji: pobierz obraz ISO systemu Windows 10 od firmy Microsoft i użyj wersji próbnej systemu Windows lub przejdź do zasobów dla programistów systemu Windows i pobierz starszą wersję VM dla programistów programu Internet Explorer. Sprawdź sekcję „Dalsze informacje”, aby uzyskać łącza. Ta ostatnia jest nadal dostarczana przez firmę Microsoft w momencie pisania, chociaż może to ulec zmianie!

UWAGA: Te testowe maszyny wirtualne są dopuszczalne do użytku laboratoryjnego, ale jeśli zamierzasz używać systemu Windows jako swojego systemu operacyjnego lub komercyjnie, musisz kupić licencję. Kradzież jest zła!

Gdy masz jedną maszynę wirtualną Windows 10 skonfigurowaną według własnych upodobań, utwórz pełny lub połączony klon. Jedna maszyna wirtualna będzie maszyną debugera, na której zainstalujesz WinDbg, a druga będzie celem debugowania. WinDbg Preview można zainstalować ze sklepu Microsoft Store, a WinDbg Classic można zainstalować z zestawu Windows SDK. Aby zainstalować WinDbg Classic, pobierz zestaw Windows SDK i wybierz Narzędzia debugowania dla systemu Windows w instalatorze. Po zakończeniu wszelkich niezbędnych instalacji włącz debugowanie jądra sieciowego, używając bcdedit z powłoki administratora na docelowej maszynie wirtualnej:

Hostip można ustawić na dowolną wartość, jeśli łączysz się przez WinDbg Preview lub określ zmienną docelową w ciągu połączenia WinDbg Classic; w przeciwnym razie ustaw ją na adres IP maszyny wirtualnej debugera. Skopiuj zwrócony klucz na maszynę debugera, ponieważ będzie on potrzebny do połączenia zdalnego. Uruchom ponownie maszynę wirtualną docelową, aby uruchomić tryb debugowania. Połącz się z WinDbg Preview, przechodząc do Plik | Dołącz do jądra, a następnie wprowadzając wymagane informacje na karcie Sieć. W przypadku WinDbg Classic lub kd.exe użyj flagi -k w wierszu poleceń i wprowadź ten ciąg połączenia, zastępując wartości w nawiasach kątowych wartościami specyficznymi dla Twojego środowiska:

Jeśli połączenie zostanie nawiązane pomyślnie, powinieneś otrzymać aktywny monit w punkcie przerwania (int 3) znajdującym się pod adresem jądra (zaczynającym się od 0xfffff). Wiersz poleceń również będzie aktywny. Jeśli nie możesz się połączyć, sprawdź adres IP celu, upewnij się, że obie maszyny wirtualne mogą połączyć się przez sieć i spróbuj wyłączyć zaporę systemu Windows na obu komputerach. Po nawiązaniu połączenia możesz poeksperymentować z kilkoma poleceniami. WinDbg jest na początku bardzo przytłaczający, ale nie martw się, z czasem staje się łatwiejszy. Po skonfigurowaniu debugowania jądra jesteś teraz gotowy, aby przejść do identyfikacji celu hakowania jądra!

Debugowanie jądra

https://chacker.pl/

Debuger w przestrzeni użytkownika (pierścień 3) jest w stanie debugować tylko poszczególne programy, które działają na jądrze. Debuger w przestrzeni jądra (pierścień 0) jest wymagany do debugowania jądra. Debugowanie jądra jest zwykle wykonywane między dwoma systemami: jeden uruchamia debuger, a drugi jest systemem, który jest debugowany. Potrzebne są dwa systemy, ponieważ w przeciwieństwie do zawieszenia pojedynczego programu w debugerze pierścienia 3, zatrzymanie całego jądra uniemożliwiłoby interakcję z systemem w celu uruchomienia poleceń lub wznowienia go! Istnieje jeden wyjątek znany jako „lokalne” debugowanie jądra, który umożliwia wygodne debugowanie jądra aktualnie uruchomionego systemu. Główną wadą lokalnego debugowania jądra jest to, że nie można zatrzymać uruchomionego systemu, co oznacza, że ​​nie można ustawić ani wstrzykiwać żadnych punktów przerwania ani debugować w przypadku awarii, a ponieważ system jest stale uruchomiony, wartości w pamięci mogą się szybko zmieniać. Jedynym oficjalnie obsługiwanym (a zatem zalecanym) debugerem pierścienia 0 dla systemu Windows jest WinDbg, który zwykle wymawia się jako win-dee-bee-gee, wind-bag lub win-dee-bug. Jest on rozwijany i utrzymywany przez firmę Microsoft i dołączany jako część pakietów narzędzi programistycznych. WinDbg oferuje szereg różnych transportów, w których można debugować jądro. Debugowanie sieciowe jest najbardziej niezawodną, ​​wydajną i spójną konfiguracją debugowania jądra. WinDbg można uzyskać, instalując zestaw Windows SDK, WDK lub ze sklepu Microsoft Store jako WinDbg Preview. Nowszy WinDbg Preview to ten sam WinDbg, ale z interfejsem podobnym do metro. Laboratoria w tej sekcji będą używać WinDbg Preview. Jeśli jesteś bardziej fanem wiersza poleceń, możesz użyć kd.exe, aby połączyć się z systemem docelowym. Jest on dołączony obok WinDbg w zestawie SDK i WDK. Wszystkie odmiany WinDbg są wspierane przez DbgEng, który stanowi podstawową funkcjonalność WinDbg. Microsoft dołącza pliki nagłówkowe i biblioteki do interakcji z DbgEng w Windows SDK, aby programiści mogli pisać narzędzia, które wykorzystują bibliotekę, która wspiera programowo WinDbg.

Sterowniki jądra

https://chacker.pl/

Sterowniki jądra to rozszerzenia jądra, które mogą pomóc systemowi w interakcji z wcześniej nieznanymi urządzeniami lub systemami plików, zapewnić interfejs do introspekcji jądra do trybu użytkownika i zmodyfikować sposób działania jądra. To ostatnie jest mocno zniechęcane przez Microsoft, tak bardzo, że firma wprowadziła Kernel Patch Protection (znany również jako PatchGuard), aby uniemożliwić programistom ingerencję w podstawowe procedury systemowe i struktury danych. Sterowniki jądra znane jako sterowniki rozruchowe są ładowane podczas rozruchu przez program ładujący. Inne sterowniki są ładowane przez menedżera usług po uruchomieniu systemu. Tylko administratorzy lub osoby z uprawnieniami SeLoadDriverPrivilege mogą ładować sterowniki w systemie Windows. Microsoft nie uważa granicy między administratorem systemu a jądrem za granicę bezpieczeństwa, ponieważ administratorzy mogą po prostu ładować (prawie) dowolne sterowniki. Jednak sterowniki muszą mieć akceptowalny podpis cyfrowy, aby mogły zostać załadowane, ponieważ podpisywanie kodu w trybie jądra (KMCS) jest wymuszane domyślnie na wszystkich maszynach 64-bitowych. Sterownik może udostępniać procedury wejścia/wyjścia (I/O) w formie głównych funkcji. Windows Driver Kit (WDK) definiuje 28 głównych funkcji, w tym tworzenie, zamykanie, zasilanie, sterowanie we/wy, odczyt, zapis, wyszukiwanie informacji, ustawianie informacji i wyłączanie. Obsługujące funkcje dla każdej głównej funkcji są ustawiane wewnątrz struktury _DRIVER_OBJECT sterownika, gdy sterownik jest inicjowany. Ta struktura zawiera różne informacje o sterowniku, takie jak nazwa sterownika, połączona lista urządzeń skojarzonych ze sterownikiem, opcjonalna procedura rozładowania, która jest wywoływana, gdy żądane jest rozładowanie sterownika, oraz ograniczenia pamięci sterownika (start i rozmiar). Sterownik może tworzyć powiązane struktury _DEVICE_OBJECT, które reprezentują urządzenie, za które odpowiada sterownik. Urządzenia mogą być lub nie być obsługiwane przez rzeczywisty sprzęt. Przykładem sterownika nieobsługiwanego sprzętowo jest sterownik, którego Sysinternal Process Explorer używa do uzyskania dodatkowych informacji o systemie. W przypadku Process Explorera sterownik podpisany przez Microsoft jest ładowany podczas uruchamiania narzędzia, a do komunikacji z nim używane są interfejsy API trybu użytkownika. Sterownik tworzy obiekt urządzenia dostępny w trybie użytkownika i obsługuje żądania z trybu użytkownika za pośrednictwem systemu I/O w jądrze. System I/O jądra wysyła żądania do głównej procedury obsługi funkcji zdefiniowanej w _DRIVER_OBJECT, do którego należy urządzenie. Główne kody funkcji to stałe wartości całkowite zdefiniowane w nagłówkach WDK. Wszystkie ich nazwy symboli zaczynają się od IRP_MJ_ i są indeksami w głównej tablicy funkcji _DRIVER_OBJECT, zaczynając od 0x70. Główne procedury obsługi funkcji są również nazywane procedurami obsługi sterowników i mają następujący prototyp:

Pakiet żądania wejścia/wyjścia (IRP) opisuje żądanie wejścia/wyjścia do urządzenia. Zawiera wiele pól, które staną się ważne, gdy będziesz pracować nad laboratorium w dalszej części rozdziału. Kilka godnych uwagi pól obejmuje pole AssociatedIrp.SystemBuffer, które często obejmuje bufor wejściowy i/lub wyjściowy dla żądania, oraz pole Tail.Overlay.CurrentStackLocation, które zawiera informacje o żądaniu odnoszące się do konkretnego wywoływanego urządzenia. Ważne informacje w CurrentStackLocation (_IO_STACK_LOCATION) obejmują pole MajorFunction, które jest bieżącą żądaną funkcją główną, oraz pole Parameters, które jest masywną unią zawierającą różne informacje w zależności od wywoływanej funkcji głównej. W przypadku sterowania wejściem/wyjściem urządzenia, pole MajorFunction będzie miało wartość IRP_MJ_DEVICE_CONTROL (14), a pole Parameters będzie opisywać wywoływany kod sterowania wejściem/wyjściem (IOCTL) oraz rozmiary buforów wejściowych i wyjściowych. W przypadku większości wywołań IOCTL bufor wejściowy i/lub wyjściowy będzie znajdował się w polu AssociatedIrp.SystemBuffer w _IRP. Aby uzyskać więcej informacji na temat kodów IOCTL, zapoznaj się z dokumentacją systemu Windows. W laboratoriach w tym rozdziale wykonasz inżynierię wsteczną i debuggowanie sterownika jądra, aby zlokalizować urządzenie, które tworzy, określisz główne programy obsługi funkcji, które są zarejestrowane, nauczysz się wywoływać główne programy obsługi funkcji z trybu ser i ostatecznie napiszesz exploit, aby wykonać lokalną eskalację uprawnień (LPE).

Jądro systemu Windows

https://chacker.pl

Ponieważ jądro systemu Windows jest tak złożone, będziemy mogli omówić tylko podstawy jądra i pewne informacje, które będą potrzebne do zrozumienia exploita w dalszej części rozdziału. Istnieje wiele bardziej kompleksowych zasobów dotyczących jądra systemu Windows i wewnętrznych mechanizmów jądra, w tym Windows Internals, 7th Edition (Parts 1 and 2), Windows Kernel Programming autorstwa Pavla Yosifovicha oraz różne posty na blogach rozsiane po Internecie. Cennymi materiałami źródłowymi są również Windows Software Development Kit (SDK), Windows Driver Kit (WDK) oraz podręczniki procesorów Intel/AMD/ARM. Ponadto będziemy przeglądać koncepcje i wykorzystywać 64-bitowy system Windows (32-bitowy system Windows jest w niektórych przypadkach nieco inny, ale z czasem staje się coraz mniej istotny). Jądro jest implementowane jako warstwa jądra, warstwa wykonawcza i sterowniki. Warstwy jądra i wykonawcze są implementowane w obrazie jądra, ntoskrnl.exe. Warstwa jądra zawiera kod do planowania wątków, blokowania, synchronizacji i podstawowego zarządzania obiektami jądra. Warstwa wykonawcza zawiera kod do egzekwowania zabezpieczeń, zarządzania obiektami, zarządzania pamięcią, rejestrowania i instrumentacji zarządzania systemem Windows, między innymi. Większość sterowników jądra to pliki .sys, ale kilka komponentów jądra to biblioteki DLL, takie jak hal.dll i ci.dll. Plik .sys to przenośny plik wykonywalny, podobnie jak plik EXE lub DLL. Poniższy diagram systemu pokazuje ogólny układ architektury systemu Windows.

Zaczynając od dołu, istnieją aplikacje i usługi w trybie użytkownika, które są uruchamiane na podsystemie Windows (kernel32.dll, user32.dll itd.), są tworzone bezpośrednio dla natywnego API (ntdll.dll i win32u.dll) lub są uruchamiane jako procesy minimalne/pico i komunikują się bezpośrednio z jądrem za pośrednictwem System Service Dispatcher. System Service Dispatcher (znany również jako system call handler) pobiera żądania z trybu użytkownika i wysyła je do jądra. Przechodząc przez linię, powinieneś zauważyć, że adresy idą od niższych w trybie użytkownika do znacznie wyższych w trybie jądra. Pamięć jest segmentowana w ten sposób z powodów historycznych i specyficznych dla procesora. Tak się składa, że ​​istnieją dwie odrębne kanoniczne przestrzenie pamięci z dużą niekanoniczną luką pośrodku, aby podzielić pamięć należącą do przestrzeni jądra (pierścień 0) i przestrzeni użytkownika (pierścień 3). Po stronie trybu jądra znajduje się warstwa jądra, warstwa wykonawcza i sterowniki, jak wspomniano wcześniej. Niektóre sterowniki, takie jak sterowniki graficzne, mogą komunikować się bezpośrednio ze sprzętem, podczas gdy inne będą używać warstwy abstrakcji sprzętowej (HAL). HAL to niezależna od architektury i platformy biblioteka do interakcji ze sprzętem. W ostatnich wersjach systemu Windows 10 (20H1+) HAL jest implementowany wewnątrz obrazu jądra, a hal.dll to po prostu przekazująca biblioteka DLL, która nadal istnieje ze względu na kompatybilność. Nie martw się, jeśli jest to dużo do przyswojenia, ponieważ jest to tylko przegląd komponentów systemu Windows.

Wykorzystanie jądra systemu Windows

https://chacker.pl/

Jądro systemu Windows i pisanie exploitów jądra to ogromne tematy; nauka wewnętrznych mechanizmów jądra i właściwego stosowania tej wiedzy w celu wykorzystania luk w zabezpieczeniach zajmuje lata. Luki te można znaleźć nie tylko w samym jądrze, ale także w rozszerzeniach znanych jako sterowniki. Tu przyjrzymy się, jak skonfigurować debugowanie jądra między dwoma systemami Windows, przeprowadzić inżynierię wsteczną sterownika jądra, a następnie wykorzystać ten sterownik jądra w celu podniesienia naszych uprawnień.

 

Podsumowanie

https://chacker.pl/

Techniki pokazane tu powinny pomóc Ci w opanowaniu podstaw eksploatacji systemu Windows za pomocą przepełnień stosu, a także omijania prostych środków zapobiegawczych, takich jak SafeSEH i DEP. Jak już widziałeś, w systemach operacyjnych Microsoft istnieją różne zabezpieczenia, w zależności od wybranych opcji kompilatora i innych czynników. Każda ochrona wiąże się z nowymi wyzwaniami dla atakujących, co skutkuje grą w kotka i myszkę. Zabezpieczenia, takie jak te oferowane przez Exploit Guard, mogą pomóc zatrzymać gotowe exploity, ale jak już wspomniano, doświadczony atakujący może dostosować exploit, aby ominąć wiele z tych kontroli.

Budowanie łańcucha ROP

https://chacker.pl/

Korzystając z wtyczki Mona PyCommand z corelanc0d3r, możemy znaleźć listę zalecanych gadżetów dla danego modułu (-cp nonull jest używane, aby upewnić się, że żadne bajty zerowe nie są używane jako część łańcuchów ROP):

Wykonanie tego polecenia powoduje utworzenie kilku plików, w tym następujących:

  • Plik rop_chains.txt zawierający ukończone lub częściowo ukończone łańcuchy ROP, których można użyć do obejścia DEP, używając funkcji takich jak VirtualProtect() i VirtualAlloc(). Te łańcuchy mogą zaoszczędzić Ci niezliczonych godzin ręcznego przeglądania i budowania łańcucha ROP.
  • Plik rop.txt zawierający dużą liczbę gadżetów, które mogą być przydatne jako część Twojego exploita. Często zdarza się, że wygenerowane łańcuchy ROP działają od razu po wyjęciu z pudełka. Często będziesz szukać gadżetów, aby zrekompensować ograniczenia, a plik rop.txt może Ci w tym pomóc.
  • Plik o nazwie stackpivot.txt, który będzie zawierał tylko instrukcje stack pivot.
  • W zależności od używanej wersji Mona, mogą zostać wygenerowane inne pliki, takie jak rop_suggestions.txt i pliki XML zawierające ukończone łańcuchy ROP. Ponadto wygenerowane łańcuchy ROP mogą się różnić w zależności od używanej wersji Mona i wybranych opcji. Więcej informacji o funkcji i jej parametrach można znaleźć na stronie Mona usage. Polecenie rop będzie działać przez chwilę i wygeneruje pliki wyjściowe do dowolnego folderu wybranego w Mona za pomocą polecenia !mona config -set workingfolder <PATH>/%p. Zawartość bardzo szczegółowego pliku rop.txt będzie zawierać wpisy takie jak ten:

Na podstawie tego wyniku możesz połączyć ze sobą gadżety, aby wykonać zadanie, budując argumenty dla VirtualProtect() i wywołując je. Nie jest to takie proste, jak się wydaje; musisz pracować z tym, co masz do dyspozycji. Być może będziesz musiał wykazać się kreatywnością. Poniższy kod, uruchomiony względem programu ProSSHD, demonstruje działający łańcuch ROP, który wywołuje VirtualProtect() w celu zmodyfikowania uprawnień, w których znajduje się kod powłoki na stosie, tak aby stał się wykonywalny. DEP został ponownie włączony dla wsshd.exe. Skrypt został nazwany prosshd_dep.py.

Chociaż na początku przestrzeganie tego programu może wydawać się trudne, gdy uświadomisz sobie, że jest to po prostu seria wskaźników do obszarów połączonych modułów, które zawierają cenne instrukcje, po których następuje instrukcja RETN, która po prostu zwraca następny gadżet, wtedy możesz zobaczyć metodę szaleństwa. Istnieje kilka gadżetów do ładowania wartości rejestrów (przygotowując się do wywołania VirtualProtect). Istnieją inne gadżety do kompensowania różnych problemów, aby zapewnić, że prawidłowe argumenty są ładowane do odpowiednich rejestrów. Podczas korzystania z łańcucha ROP wygenerowanego przez Mona, ten autor ustalił, że po prawidłowym wyrównaniu wywołanie VirtualProtect() jest pomyślnie wykonywane; jednak po powrocie z SYSEXIT z Ring0, wracamy zbyt daleko w dół stosu i do środka naszego kodu powłoki. Aby to zrekompensować, ręcznie dodano kilka gadżetów, aby upewnić się, że EBP wskazuje na nasz NOP sled. Można by poświęcić czas na precyzyjne wyrównanie rzeczy, aby tak duże wypełnienie nie było konieczne; jednak ten czas można również poświęcić na inne zadania. Wygenerowany łańcuch ROP może wyglądać zupełnie inaczej niż ten pokazany w tym przykładzie.

W poniższym kodzie najpierw umieszczamy wartość 0xfffffcdf w EAX. Gdy zostanie ona dodana do adresu w EBP, który wskazuje na nasz kod powłoki, zostanie ona przeniesiona o 2^32 i będzie wskazywała na nasz sled NOP.

Aby to obliczyć, wystarczy wykonać podstawowe obliczenia matematyczne, aby upewnić się, że EBP wskazuje na lokalizację wewnątrz sanek NOP. Ostatnia instrukcja wykonuje to dodawanie. Aby zademonstrować stan przed i po, spójrz na poniższe obrazy.

Na tym pierwszym obrazku program jest wstrzymany przed dostosowaniem do EBP. Jak widać, EBP wskazuje na środek shellcode. Następny obraz pokazuje adres, na który wskazuje EBP po dokonaniu dostosowania.

Jak widać, EBP wskazuje na nasz NOP sled, tuż przed shellcode. Shellcode używany w exploicie, wygenerowany za pomocą Metasploit, wiąże shell z portem TCP 31337. Gdy exploitowi pozwolono kontynuować, shellcode jest pomyślnie wykonywany, a port jest otwarty, jak pokazano tutaj z monitem zapory.