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.

Gadżety

https://chacker.pl/

Małe sekcje kodu wymienione w poprzedniej sekcji to to, co nazywamy gadżetami. Słowo kod jest tutaj używane, ponieważ nie musi to być instrukcja używana przez program lub moduł; możesz przejść do adresu w środku zamierzonej instrukcji lub gdziekolwiek indziej w pamięci wykonywalnej, pod warunkiem, że wykonuje ona zadanie, które chcesz wykonać i zwraca wykonanie do następnego gadżetu wskazywanego przez wskaźnik stosu. Poniższy przykład pokazuje zamierzoną instrukcję użytą wewnątrz ntdll.dll pod adresem pamięci 0x778773E2:

Zobacz co się stanie, gdy przejdziemy z 0x778773E2 do 0x778773E3:

Sekwencja kodu nadal kończy się return, ale instrukcja nad return została zmieniona. Jeśli ten kod jest dla nas zrozumiały, możemy go użyć jako gadżetu. Ponieważ następny adres wskazywany przez ESP lub RSP na stosie jest innym gadżetem ROP, polecenie return wywołuje tę następną sekwencję kodu. Ponownie, ta metoda programowania jest podobna do ret2libc i jest w rzeczywistości jej następcą, jak omówiono w rozdziale 10. W ret2libc nadpisujemy wskaźnik return adresem początku funkcji, takiej jak system(). W ROP, gdy tylko uzyskamy kontrolę nad wskaźnikiem instrukcji, wskazujemy mu lokalizację wskaźników do naszych gadżetów i wracamy przez łańcuch. Niektóre gadżety zawierają niechciane instrukcje, które musimy zrekompensować, takie jak POP lub inna instrukcja, która mogłaby negatywnie zmodyfikować stos lub rejestr. Spójrz na deasemblację:

Sekwencja kodu nadal kończy się return, ale instrukcja nad return została zmieniona. Jeśli ten kod jest dla nas zrozumiały, możemy go użyć jako gadżetu. Ponieważ następny adres wskazywany przez ESP lub RSP na stosie jest innym gadżetem ROP, polecenie return wywołuje tę następną sekwencję kodu. Ponownie, ta metoda programowania jest podobna do ret2libc i jest w rzeczywistości jej następcą, jak omówiono w rozdziale 10. W ret2libc nadpisujemy wskaźnik return adresem początku funkcji, takiej jak system(). W ROP, gdy tylko uzyskamy kontrolę nad wskaźnikiem instrukcji, wskazujemy mu lokalizację wskaźników do naszych gadżetów i wracamy przez łańcuch. Niektóre gadżety zawierają niechciane instrukcje, które musimy zrekompensować, takie jak POP lub inna instrukcja, która mogłaby negatywnie zmodyfikować stos lub rejestr. Spójrz na deasemblację:

W tym przykładzie po prostu zmieniliśmy POP EDI na POP EAX. Jeśli naszym celem jest wyzerowanie rejestru EAX, niechciany POP EAX sprawi, że ten gadżet stanie się bezużyteczny. Istnieją inne typy niechcianych instrukcji, z których niektóre mogą być dość trudne do rozwiązania, takie jak dostęp do adresu pamięci, który nie jest mapowany.

 

Programowanie zorientowane na zwrot

https://chacker.pl/

Co więc możemy zrobić, jeśli nie możemy wykonać kodu na stosie? Wykonać go gdzie indziej? Ale gdzie? W istniejących powiązanych modułach jest wiele małych sekwencji kodu, które kończą się instrukcją RETN. Te sekwencje kodu mogą lub nie być wykonywane przez program. Wyobraźmy sobie, że mamy kontrolę nad procesem za pośrednictwem przepełnienia bufora. Jeśli ułożymy serię wskaźników do tych pożądanych sekwencji kodu, na które wskazuje wskaźnik stosu, i wrócimy do każdego z nich po kolei, możemy zachować kontrolę nad procesem i sprawić, by wykonywał nasze polecenia. Nazywa się to programowaniem zorientowanym na zwrot i zostało zapoczątkowane przez Hovava Shachama. Jest to następca technik takich jak ret2libc. Możemy użyć tych gadżetów, aby skonfigurować wywołanie funkcji w celu zmiany uprawnień w pamięci, w której znajduje się nasz kod powłoki, co pozwala nam obejść DEP.

Zapobieganie wykonywaniu danych

https://chacker.pl/

Zapobieganie wykonywaniu danych (DEP) ma na celu zapobieganie wykonywaniu kodu umieszczonego w stercie, stosie i innych sekcjach pamięci, w których wykonywanie kodu nie powinno być dozwolone. Przed rokiem 2004 sprzęt nie obsługiwał tego. W roku 2004 AMD wprowadziło bit NX w swoim procesorze. Po raz pierwszy pozwoliło to sprzętowi rozpoznać stronę pamięci jako wykonywalną lub nie i odpowiednio zareagować. Niedługo potem Intel wprowadził funkcję XD, która robiła to samo. System Windows mógł używać bitu NX/XD od czasu XP SP2 i jest on uważany za dojrzałą i skuteczną kontrolę. Aplikacje można łączyć z flagą /NXCOMPAT, która włączy sprzętowy DEP dla tej aplikacji, w zależności od wersji systemu operacyjnego i obsługi różnych krytycznych funkcji związanych z uprawnieniami i zabezpieczeniami pamięci.

Omijanie SafeSEH

https://chacker.pl/

Jak już wcześniej wspomniano, gdy wyzwolony zostanie wyjątek, system operacyjny umieszcza funkcję except_handler na stosie i wywołuje ją.

Po pierwsze, zauważ, że gdy wyjątek jest obsługiwany, wskaźnik _EstablisherFrame jest przechowywany w ESP+8. Wskaźnik _EstablisherFrame wskazuje w rzeczywistości na szczyt naszego łańcucha obsługi wyjątków. Dlatego jeśli zmienimy wskaźnik _next naszego nadpisanego rekordu wyjątku na instrukcję asemblera EB 06 90 90 (która przeskoczy o 6 bajtów do przodu) i zmienimy wskaźnik _handler gdzieś w udostępnionym pliku DLL/EXE, w sekwencji POP/POP/RETN, możemy przekierować kontrolę programu do naszego obszaru kodu atakującego na stosie. Gdy wyjątek jest obsługiwany przez system operacyjny, zostanie wywołany program obsługi, który rzeczywiście zdejmie 8 bajtów ze stosu i wykona instrukcję wskazywaną na ESP+8 (która jest naszym poleceniem JMP 06), a kontrola zostanie przekierowana do obszaru kodu atakującego na stosie, gdzie może zostać umieszczony kod powłoki.

UWAGA: W tym przypadku musieliśmy przeskoczyć tylko o 6 bajtów do przodu, aby wyczyścić następujący adres i 2 bajty instrukcji skoku. Czasami, ze względu na ograniczenia przestrzeni, może być potrzebny skok wstecz na stosie; w takim przypadku, można użyć liczby ujemnej, aby przeskoczyć wstecz (na przykład EB FA FF FF przeskoczy wstecz o 6 bajtów).

Bezpieczna strukturalna obsługa wyjątków

https://chacker.pl/

Celem ochrony Bezpieczna strukturalna obsługa wyjątków (SafeSEH) jest zapobieganie nadpisywaniu i używaniu struktur SEH przechowywanych na stosie. Jeśli program jest kompilowany i linkowany z opcją linkera /SafeSEH, nagłówek tego pliku binarnego będzie zawierał tabelę wszystkich prawidłowych obsługi wyjątków; tabela ta będzie sprawdzana, gdy zostanie wywołana obsługa wyjątków, aby upewnić się, że znajduje się ona na liście. Sprawdzanie jest wykonywane jako część procedury RtlDispatchException w ntdll.dll, która wykonuje następujące testy:

  • Zapewnia, że ​​rekord wyjątku znajduje się na stosie bieżącego wątku.
  • Zapewnia, że ​​wskaźnik obsługi nie wskazuje z powrotem na stos.
  • Zapewnia, że ​​obsługa jest zarejestrowana na autoryzowanej liście obsługi.
  • Zapewnia, że ​​obsługa znajduje się w obrazie pamięci, który jest wykonywalny.

Jak widać, mechanizm ochrony SafeSEH podejmuje kroki w celu ochrony obsługi wyjątków, ale jak zaraz zobaczysz, nie jest on niezawodny.

Zrozumienie i omijanie typowych zabezpieczeń pamięci systemu Windows

https://chacker.pl/

Jak można było się spodziewać, z czasem atakujący nauczyli się wykorzystywać brak zabezpieczeń pamięci w poprzednich wersjach systemu Windows. W odpowiedzi, mniej więcej w czasie Windows XP SP2 i Server 2003, Microsoft zaczął dodawać zabezpieczenia pamięci, które przez pewien czas były dość skuteczne. Jednak atakujący ostatecznie nauczyli się również obejść te początkowe zabezpieczenia. Jest to ciągła ewolucja technik eksploatacji i zabezpieczeń w celu udaremnienia sukcesu tych technik. Na przestrzeni lat dodano wiele nowych zabezpieczeń, a nawet zestawy narzędzi łagodzących, takie jak Windows Defender Exploit Guard, który zadebiutował w systemie Windows 10 w wersji 1709. Gdy te zabezpieczenia są połączone, mogą one znacznie utrudnić wykorzystanie luki.

Zrozumienie obsługi wyjątków strukturalnych

https://chacker.pl/

Gdy programy ulegają awarii, system operacyjny udostępnia mechanizm o nazwie Obsługa wyjątków strukturalnych (SEH), aby spróbować odzyskać operacje. Jest to często implementowane w kodzie źródłowym za pomocą bloków try/catch lub try/exception:

System Windows śledzi rekordy SEH, korzystając ze specjalnej struktury:

Struktura EXCEPTION_REGISTRATION ma rozmiar 8 bajtów i zawiera dwa elementy:

  • prev Wskaźnik do następnego rekordu SEH
  • handler Wskaźnik do rzeczywistego kodu obsługi

Te rekordy (ramki wyjątku) są przechowywane na stosie w czasie wykonywania i tworzą łańcuch. Początek łańcucha jest zawsze umieszczany w pierwszym elemencie bloku informacji o wątku (TIB), który jest przechowywany na maszynach x86 w rejestrze FS:[0]. Jak pokazano na rysunku , końcem łańcucha jest zawsze domyślny systemowy program obsługi wyjątku, a wskaźnik prev tego rekordu EXCEPTION_REGISTRATION jest zawsze równy 0xFFFFFFFF.

Gdy zostanie wyzwolony wyjątek, system operacyjny (ntdll.dll) umieszcza następującą funkcję C++ na stosie i wywołuje ją:

W przeszłości atakujący mógł po prostu nadpisać jeden z programów obsługi wyjątków na stosie i przekierować kontrolę do kodu atakującego (na stosie).

Jednak później pewne rzeczy uległy zmianie:

  • Rejestry są zerowane tuż przed wywołaniem programów obsługi wyjątków.
  • Wywołania programów obsługi wyjątków, znajdujących się na stosie, są blokowane.

Łańcuch SEH może być interesującym celem, ponieważ często, nawet jeśli nadpisujesz wskaźnik powrotu na stosie, wykonanie nigdy nie osiąga instrukcji powrotu. Jest to zwykle spowodowane naruszeniem dostępu do odczytu lub zapisu, które ma miejsce przed osiągnięciem epilogu funkcji, spowodowane dużą liczbą znaków wysłanych do bufora, które nadpisały krytyczne dane. W takim przypadku dalej w dół stosu za buforem znajduje się lokalizacja łańcucha SEH dla wątku. Naruszenie dostępu do odczytu lub zapisu spowoduje, że FS:[0] zostanie poddane getdereferenced, co zawiera wskaźnik do adresu stosu wątku, gdzie przechowywana jest pierwsza wartość „Next SEH” (NSEH). Rejestr segmentu FS zawsze wskazuje na Thread Information Block (TIB) dla aktualnie aktywnego wątku. TIB to struktura użytkownika na wątek, która przechowuje dane, takie jak wskaźnik do początku łańcucha SEH dla wątku w FS:[0], limity stosu i wskaźnik do Process Environment Block (PEB) w FS:[0x30]. Bezpośrednio pod pozycją NSEH na stosie znajduje się adres pierwszego wywołanego programu obsługi. Nadpisanie tego adresu adresem niestandardowym jest często łatwym sposobem na uzyskanie kontroli, jeśli nie można tego zrobić za pomocą nadpisywania wskaźnika powrotu. SafeSEH ma na celu uniemożliwienie działania tej techniki, ale jak zobaczysz, można ją łatwo ominąć.

Debugowanie Exploitu, jeśli to konieczne

https://chacker.pl/

Czas zresetować system wirtualny i uruchomić poprzedni skrypt. Pamiętaj, aby szybko podłączyć się do wsshd.exe i nacisnąć klawisz F9, aby uruchomić program. Pozwól programowi osiągnąć początkowy wyjątek. Kliknij w dowolnym miejscu sekcji demontażu i naciśnij klawisze CTRL-G, aby wyświetlić okno dialogowe Wprowadź wyrażenie do wykonania. Wprowadź adres z Mona, którego używasz do przejścia do ESP, jak pokazano poniżej. W tym przykładzie było to 0x7c345c30 z MSVCR71.dll. Naciśnij klawisz F9, aby osiągnąć punkt przerwania.

Jeśli program ulega awarii zamiast osiągnąć punkt przerwania, istnieje prawdopodobieństwo, że masz zły znak w kodzie powłoki lub w skrypcie występuje błąd. Problemy ze złymi znakami zdarzają się od czasu do czasu, ponieważ podatny program (lub program klienta SCP w tym przypadku) może reagować na pewne znaki i powodować przerwanie lub inną modyfikację exploita. Aby znaleźć zły znak, musisz przejrzeć zrzut pamięci debugera i dopasować ten zrzut pamięci do rzeczywistego kodu powłoki wysłanego przez sieć. Aby skonfigurować tę inspekcję, musisz powrócić do systemu wirtualnego i ponownie wysłać skrypt ataku. Gdy zostanie osiągnięty początkowy wyjątek, kliknij sekcję stosu i przewiń w dół, aż zobaczysz litery A. Kontynuuj przewijanie w dół, aby znaleźć kod powłoki, a następnie wykonaj ręczne porównanie. Innym prostym sposobem wyszukiwania złych znaków jest wysyłanie wszystkich możliwych kombinacji pojedynczego bajtu sekwencyjnie jako danych wejściowych. Możesz założyć, że 0x00 to zły znak, więc wpisz coś takiego:

UWAGA: Może być konieczne wielokrotne powtórzenie procesu wyszukiwania złych znaków, aż kod zostanie wykonany poprawnie. Generalnie należy wykluczyć wszystkie znaki odstępu: 0x00, 0x20, 0x0a, 0x0d, 0x1b, 0x0b i 0x0c. Należy wykluczać po jednym znaku na raz, aż wszystkie oczekiwane bajty pojawią się w segmencie stosu.

Gdy to będzie działać poprawnie, należy osiągnąć punkt przerwania ustawiony w instrukcjach PUSH ESP i RETN. Naciśnij klawisz F7, aby wykonać pojedynczy krok. Wskaźnik instrukcji powinien teraz wskazywać na wypełnienie NOP. Krótkie sanki lub wypełnienie powinny być widoczne w sekcji deasemblera, jak pokazano tutaj:

Naciśnij F9, aby kontynuować wykonywanie. Na ekranie powinien pojawić się kalkulator, jak pokazano poniżej, pokazując w ten sposób wykonanie kodu powłoki w naszym działającym exploicie! Zademonstrowaliśmy teraz podstawowy proces rozwoju exploita Windows na rzeczywistym exploicie.

Więliśmy podatną na ataki aplikację Windows i napisaliśmy działający exploit, aby naruszyć system docelowy. Celem było zwiększenie Twojej znajomości Immunity Debugger i wtyczki Mona od Corelan Team, a także wypróbowanie podstawowych technik powszechnie stosowanych przez twórców exploitów w celu skutecznego naruszenia bezpieczeństwa aplikacji. Identyfikując moduły, które nie uczestniczyły w różnych kontrolach ograniczających ataki, takich jak ASLR, byliśmy w stanie użyć ich do uzyskania niezawodnego exploita. Następnie przyjrzymy się bliżej różnym zabezpieczeniom pamięci i technikom obejścia.