Przenośność programu PowerShell

https://chacker.pl/

Jednym z miłych aspektów programu PowerShell jest to, że moduły są bardzo przenośne i można je ładować na wiele różnych sposobów. Daje nam to możliwość ładowania zarówno modułów zainstalowanych w systemie, jak i modułów w innych lokalizacjach. Możemy również ładować moduły z udziałów Server Message Block (SMB), a także z sieci Web. Dlaczego możliwość ładowania z tych zdalnych lokalizacji jest tak cenna? Chcemy pozostawić jak najmniej śladów i chcemy powielać jak najmniej pracy. Oznacza to, że możemy pozostawić elementy, których będziemy często używać, w udziale SMB lub nawet w witrynie, a następnie odwoływać się do nich z tego miejsca. Ponieważ skrypt jest tylko tekstem, nie musimy martwić się o bloki dla plików binarnych lub podobnych typów plików. Możemy również zaciemniać kod, a następnie dekodować go w locie, co potencjalnie ułatwia omijanie kontroli bezpieczeństwa. Ponieważ skrypt jest tylko tekstem, możemy go uwzględnić niemal wszędzie. Często witryny z kodem, takie jak GitHub, są przydatne do tego typu działań, ponieważ takie witryny mają wiele celów związanych z biznesem. Możemy uwzględnić nasze skrypty w repozytorium lub jako podstawowe polecenia gist, które ładujemy z naszego środowiska PowerShell, aby uruchomić inne działania. PowerShell może nawet używać ustawień proxy użytkownika, więc jest to świetny sposób na ustanowienie trwałości w środowisku.

Rejestrowanie bloków skryptów

https://chacker.pl/

Rejestrowanie bloków skryptów służy do rejestrowania, kiedy wykonywane są bloki skryptów, co zapewnia większą głębię w tym, co jest wykonywane. Począwszy od programu PowerShell v5.0, rejestrowanie bloków skryptów dostarcza wielu danych o potencjalnie podejrzanych zdarzeniach, aby dać ludziom zajmującym się analizą kryminalistyczną coś do zrobienia. Rejestrowane elementy obejmują skrypty uruchomione za pomocą opcji encodedcommand, a także wszelkie podstawowe zaciemnianie. Dlatego po włączeniu rejestrowania bloków skryptów obrońcy prawdopodobnie będą mieli dodatkowe informacje na temat tego, co robiłeś. Jest to lepsze rozwiązanie dla obrońców niż rejestrowanie modułów, ponieważ wyróżnia rzeczy, które prawdopodobnie byłyby dla Ciebie ważne z punktu widzenia analizy kryminalistycznej, a jednocześnie nie powoduje tak dużego obciążenia analizą dziennika.

 

Rejestrowanie modułów

https://chacker.pl/

Rejestrowanie modułów umożliwia szereg funkcji dotyczących sposobu ładowania skryptów i podstawowych informacji o tym, co zostało wykonane. Obejmuje to, jakie moduły i zmienne zostały załadowane, a nawet niektóre informacje o skrypcie. Rejestrowanie to znacznie zwiększa szczegółowość podczas uruchamiania skryptów programu PowerShell i może być przytłaczające dla administratora. Rejestrowanie modułów jest dostępne od wersji PowerShell v3.0 i nie jest domyślnie włączone, więc aby uzyskać to rejestrowanie, należy włączyć obiekt zasad grupy (GPO) w systemach. Chociaż ten typ rejestrowania zwiększa widoczność tego, co zostało uruchomione, w większości przypadków nie zapewnia faktycznego kodu, który został uruchomiony. Dlatego też w przypadku dochodzenia kryminalistycznego ten poziom rejestrowania jest nadal niewystarczający. Będzie jednak naprowadzał śledczych na typy rzeczy, które robiłeś, chociaż szczegóły prawdopodobnie nie zostaną zarejestrowane.

Rejestrowanie w programie PowerShell

https://chacker.pl/

We wcześniejszych wersjach programu PowerShell (przed v4.0) dostępnych było tylko kilka opcji rejestrowania. Pozwalało nam to działać bez tworzenia wielu alertów dziennika podczas ładowania programu PowerShell, a także bardzo utrudniało osobom zajmującym się analizą kryminalistyczną ustalenie, co robiliśmy. Rejestrowanie rejestrowało tylko fakt załadowania programu PowerShell. W nowszych wersjach programu PowerShell dostępne są dodatkowe opcje zwiększające rejestrowanie w programie PowerShell. Z tego powodu ukierunkowanie na najnowszą wersję systemu Windows może zdradzić więcej informacji o tym, co robisz, niż w starszych wersjach.

UWAGA: Omawiamy tylko kilka aspektów rejestrowania w programie PowerShell, które mogą mieć wpływ na wykrywanie włamań. Aby uzyskać więcej informacji, dodaliśmy odniesienie z FireEye, które bardziej szczegółowo opisuje różne opcje i wyjaśnia, jak je włączyć.

Życie z ziemi

https://chacker.pl/

Kiedy mówimy o „życiu z ziemi”, mamy na myśli korzystanie z narzędzi już obecnych w systemach w celu dalszego wykorzystania luk. Jest to cenne, ponieważ zawsze, gdy dodajemy coś do systemu, zwiększamy prawdopodobieństwo wykrycia. Co więcej, gdy zostawiamy narzędzia, pomaga to ujawnić nasze taktyki, techniki i procedury (TTP), dzięki czemu łatwiej jest znaleźć naszą aktywność w innych systemach. Gdy żyjemy z ziemi, możemy zostawić mniej artefaktów i ograniczyć narzędzia, które musimy przenosić z systemu do systemu. PowerShell jest przydatny jako już istniejące narzędzie w systemie, ponieważ daje nam możliwość łatwego pisania skryptów, a także obejmuje integrację z .NET, więc prawie wszystko, co możemy napisać w .NET, możemy napisać w PowerShell. Oznacza to, że możemy wyjść poza podstawowe skrypty i wchodzić w interakcje z funkcjami jądra i nie tylko. Daje nam to dodatkową elastyczność, która normalnie wymagałaby użycia oddzielnych programów. Jedną z głównych zalet PowerShell jest to, że może on korzystać z opcji przeglądarki Internet Explorer, więc takie rzeczy jak obsługa proxy są wbudowane w PowerShell. W rezultacie możemy używać wbudowanych bibliotek internetowych do zdalnego ładowania kodu, co oznacza, że ​​nie musimy ręcznie pobierać żadnego kodu do systemu docelowego. Dlatego gdy ktoś spojrzy na oś czasu systemu plików, te pobrania ze stron internetowych nie będą widoczne, co pozwoli nam być jeszcze bardziej dyskretnym.

Dlaczego PowerShell

https://chacker.pl/

Chociaż język PowerShell był błogosławieństwem dla automatyzacji systemów Windows, daje hakerom przewagę. PowerShell zapewnia nam dostęp do niemal wszystkich funkcji systemu Windows w sposób programowy. Jest rozszerzalny i może być używany do administrowania usługą Active Directory, systemami poczty e-mail, programem SharePoint, stacjami roboczymi i nie tylko. PowerShell zapewnia nam dostęp do bibliotek .NET z poziomu interfejsu skryptowego, co czyni go jednym z najbardziej elastycznych narzędzi, których można używać w środowisku Windows.

Wykorzystanie PowerShell

https://chacker.pl/

Większość systemów korporacyjnych opiera się na systemie Windows, dlatego ważne jest, abyśmy dobrze znali narzędzia dostępne w systemach Windows. Jednym z najpotężniejszych z tych narzędzi jest PowerShell. W tym rozdziale dowiesz się, co sprawia, że ​​PowerShell jest tak potężnym narzędziem, i przyjrzymy się kilku sposobom jego wykorzystania jako części naszego zestawu narzędzi do eksploatacji luk.

Podsumowanie

https://chacker.pl/

Jądro systemu Windows może być trudne, ale możliwe do opanowania przy odpowiednich zasobach i pod ręką debugera. Nieudokumentowana natura samego jądra sprawia, że ​​badanie lub wykorzystywanie go jest jeszcze bardziej czasochłonne. W laboratoriach skonfigurowałeś debugowanie jądra, wybrałeś znany podatny sterownik jądra jako cel, wykonałeś inżynierię wsteczną sterownika, napisałeś narzędzie do interakcji ze sterownikiem, a następnie napisałeś exploit LPE, wykorzystując kradzież tokenów za pośrednictwem funkcjonalności w sterowniku. Miejmy nadzieję, że dało Ci to punkt wyjścia do rozpoczęcia dalszych badań jądra!

 

Pisanie exploita jądra

https://chacker.pl/

Kontynuując pracę nad naszym kodem exploita, musimy znaleźć bazę jądra i symbol PsInitialSystemProcess. Ponieważ zakładamy, że mamy dostęp na poziomie użytkownika do tego exploita, możemy poprosić system, aby powiedział nam, gdzie znajduje się baza każdego załadowanego sterownika za pomocą funkcji EnumDeviceDrivers, a następnie możemy uzyskać nazwę sterownika pod każdym adresem bazowym za pomocą funkcji GetDeviceDriverBaseNameA:

Tutaj jest sporo do rozpakowania! Pierwsze wywołanie numDeviceDrivers umieszcza wymagany rozmiar bufora (w bajtach) w needed (1). Następnie bufor jest przydzielany do przechowywania oczekiwanego wyjścia (2), a bufor ten jest wypełniany poprzez drugie wywołanie EnumDeviceDrivers (3). Następnie adresy bazowe są iterowane, a nazwa każdego z nich jest pobierana poprzez GetDeviceDriverBaseNameA (4). Namebuf ma długość 260 bajtów (5), co możesz rozpoznać jako MAX_PATH; powinno to wystarczyć, aby zmieścić nazwę sterownika. Jeśli nazwa pasuje do ntoskrnl.exe (6), wówczas baza w tej bieżącej iteracji może zostać zwrócona jako baza jądra (7). Ponownie, ta technika działa tylko dla LPE o średniej integralności lub lepszej. Zdalne i/lub eksploity o niskiej integralności muszą znaleźć inny sposób na uzyskanie wskaźnika _EPROCESS, np. poprzez wyciek pamięci i dowolny prymityw odczytu. Na koniec możemy skonstruować eksploit. Utwórz plik w katalogu src/bin swojego projektu o nazwie exploit.rs i dodaj następujące elementy:

W głównych nawiasach najpierw wywołaj funkcję open_dev, aby uzyskać HANDLE do urządzenia. Ponieważ zadeklarowaliśmy tę funkcję jako unsafe, wywołanie musi zostać opakowane w blok unsafe:

Jako ćwiczenie wstaw po wywołaniu LoadLibraryA następujący kod:

Spowoduje to wstrzymanie programu do momentu naciśnięcia klawisza, co da Ci czas na zbadanie programu. Zdefiniuj to jako funkcję, jeśli chcesz wstrzymać program w wielu punktach. Uruchom program za pomocą cargo run –bin exploit. Następnie załaduj Sysinternals Process Explorer, znajdź proces exploita i otwórz dolny panel widoku DLL. Wyszukaj „ntoskrnl.exe” i zwróć uwagę, że adres bazowy jest adresem w trybie użytkownika. Obraz jądra, do którego odwołujesz się jako hkernel, jest tą kopią w trybie użytkownika, a nie tą w działającym jądrze. Aby uzyskać adres PsInitialSystemProcess w działającym jądrze, najpierw znajdziemy względny adres wirtualny (RVA) symbolu. RVA to po prostu przesunięcie symbolu od podstawy obrazu. Aby je obliczyć, możemy odjąć adres bazowy modułu (hkernel) (2) od adresu PsInitialSystemProcess wewnątrz kopii jądra w trybie użytkownika. W trybie użytkownika GetProcAddress (1) może zostać użyte do znalezienia dowolnego wyeksportowanego symbolu w załadowanym obrazie PE, więc możemy użyć go do znalezienia adresu symbolu. Aby uzyskać adres, którego potrzebujemy w działającym jądrze, dodaj obliczony RVA do wartości zwracanej przez get_kernel_base (3). Ponieważ każda operacja w tym procesie wymaga znacznika unsafe, możemy uwzględnić go w tym samym bloku i zakończyć go adresem PsInitialSystemProcess w działającym jądrze:

UWAGA: Brakujący średnik na końcu ostatniego wiersza, w punkcie (3), nie jest literówką i jest zamierzony. W Rust wiersz, który nie kończy się średnikiem, jest zwracany z tego bloku. W tym przypadku wartość ostatniego wiersza jest umieszczana w zmiennej lpisp. Ponieważ wartość w lpisp jest po prostu wskaźnikiem do PsInitialSystemProcess, następną rzeczą, którą musimy zrobić, jest wykorzystanie dowolnego prymitywu odczytu, aby pobrać adres wewnątrz niego:

Używa to dowolnego odczytu jądra, aby uzyskać adres struktury _EPROCESS reprezentującej proces SYSTEM (PID 4). Możesz chcieć sprawdzić, czy wartość jest poprawna. Aby to zrobić, dodaj polecenie print i pauzę (jak opisano wcześniej), a następnie zrzuć wartość wewnątrz PsInitialSystemProcess w debugerze za pomocą polecenia dq nt!PsInitialSystemProcess L1. Ponieważ kradniemy tokeny z procesu SYSTEM, następnym krokiem jest odczytanie pola Token _EPROCESS za pomocą dowolnego odczytu. W tym momencie powinieneś sprawdzić przesunięcia od podstawy struktury _EPROCESS dla pól UniqueProcessId, ActiveProcessLinks i Token. Można to łatwo zrobić w debugerze jądra za pomocą następującego polecenia:

Następnie zdefiniuj te stałe w górnej części pliku exploits.rs

Teraz odczytaj token SYSTEM z procesu systemowego za pomocą dowolnego wskaźnika:

Następny krok jest nieco bardziej skomplikowany, ponieważ teraz musimy przejść listę procesów przez podwójnie łączoną listę ActiveProcessLinks, aby znaleźć aktualnie wykonywany proces. Przechodzenie listy procesów jest nieco trudne, ponieważ lista ActiveProcessLinks nie wskazuje na początek następnego procesu; zamiast tego wskazuje na strukturę ActiveProcessLinks w następnym procesie! Aby to rozwiązać, musimy odczytać wartość w ActiveProcessLinks (2), a następnie odjąć przesunięcie ActiveProcessLinks od tej wartości, aby dostać się na początek struktury _EPROCESS dla następnego procesu na liście (3). Następnie, gdy już jesteśmy w następnym procesie, odczytujemy (4) i porównujemy pole UniqueProcessId z bieżącym identyfikatorem procesu (1). Jeśli identyfikator procesu jest zgodny, bieżący proces został znaleziony i możemy przejść do ostatniego kroku exploita. Jeśli bieżący proces nie zostanie znaleziony, program musi przejść do następnego procesu na liście i kontynuować, aż zostanie znaleziony.

W tym momencie pozostało już tylko skopiowanie tokena SYSTEM do pola Token struktury _EPROCESS bieżącego procesu, którą właśnie znaleźliśmy w ostatnim kroku. Aby to zrobić, użyj dowolnej funkcji zapisu wskaźnika, którą napisaliśmy w poprzednim laboratorium. Po nadpisaniu tokena utwórz podproces, taki jak cmd.exe:

Jeśli wszystko pójdzie dobrze, exploit powinien utworzyć powłokę i nie spowodować awarii komputera. Uruchom whoami, jak pokazano poniżej, aby sprawdzić, czy masz SYSTEM!

Możesz napotkać problemy z tym, że program Windows Defender nazwie Twój exploit malware. To malware, więc program Defender po prostu wykonuje swoją pracę! Upewnij się jednak, że po kliknięciu opcji Zezwalaj na urządzeniu wybierasz exploit z listy, a nie rzeczywisty fragment malware.

Arbitralny odczyt/zapis wskaźnika

https://chacker.pl/

Po dokładnym zrozumieniu celu kradzieży tokenów musimy złożyć elementy w całość, aby utworzyć w pełni funkcjonalny exploit eskalacji uprawnień lokalnych. Ponieważ kradzież tokenów obejmuje tylko odczyt i zapis wartości o rozmiarze wskaźnika, możemy uniknąć kłopotów związanych ze wskaźnikami struktury, dodając element usize na końcu struktury DbMemmove:

Teraz możemy napisać jedną funkcję do odczytu wskaźnika w jądrze i inną do zapisu wskaźnika w jądrze. Funkcja odczytu musi przyjąć HANDLE do urządzenia DBUtil i adres do odczytu, a następnie zwrócić zawartość tego adresu:

Ponieważ wyprowadziliśmy Default w definicji struktury, możemy wypełnić jedno pole wymagane do odczytu (1), a następnie zaakceptować wartość domyślną dla pozostałych (0 dla typów całkowitych) (2). Następnie otrzymujemy zmienny surowy wskaźnik do struktury (3), który należy przekazać do funkcji ioctl jako bufor wraz z uchwytem urządzenia, kodem IOCTL dla dowolnego odczytu i rozmiarem struktury (4). Na koniec zwracamy wartość wyjściową (5). Funkcja write musi również przyjąć DBUtil HANDLE, wskaźnik do zapisu, a następnie wartość do zapisu do tego wskaźnika. Ma on bardzo podobny format do poprzedniej funkcji; tym razem wypełnimy element danych struktury i wywołamy funkcję ioctl z dowolnym kodem IOCTL zapisu:

Dzięki tym dwóm funkcjom w naszej bibliotece mamy teraz dwa bardzo wydajne prymitywy i prawie wszystkie składniki, których potrzebujemy, aby podnieść uprawnienia zwykłego użytkownika do SYSTEM.