Żądny wiedzy? Wbijaj na Mega Sekurak Hacking Party w maju! -30% z kodem: majearly

Poznaj bezpieczeństwo Windows – lokalne uwierzytelnianie i autoryzacja [notatki ze szkolenia]

31 maja 2023, 09:42 | W biegu | komentarze 4
TL;DR – jeden z uczestników szkolenia: Poznaj bezpieczeństwo Windows. Część druga: lokalne uwierzytelnianie i autoryzacja przygotował rozbudowane notatki, które publikujemy poniżej. Otwarte są już zapisy na część trzecią szkolenia: Tajniki Group Policy (szkolenia te dostępne są również w ramach projektu: Sekurak Academy)

Uwierzytelnienie vs. autoryzacja

Różnica pomiędzy uwierzytelnieniem (ang. authentication) i autoryzacją (ang. authorization) została już wyjaśniona w niejednej publikacji, więc tylko sobie przypomnijmy: proces uwierzytelnienia potwierdza, że na pewno jesteśmy osobą, za którą się podajemy, a autoryzacja sprawdza, do czego mamy dostęp jako ta osoba.

Intuicyjnym przykładem jest budynek firmy i pracownik, któremu wydano kartę dostępu, kiedy podpisywał umowę (uwierzytelnienie). Posiadacz tej karty może wejść do budynku, ale ma dostęp jedynie do działów, z którymi jest w jakiś sposób związany (autoryzacja). Do innych działów dostępu nie ma i jego karta nie zadziała.

Uwierzytelnienie kont lokalnych

Poniżej skupimy się na najważniejszym wycinku całej procedury logowania, związanym z LSA. Jeśli jednak kogoś interesuje trochę szersze spojrzenie na to, jak przebiega proces uwierzytelnienia użytkownika lokalnego podczas uruchomienia systemu, może zapoznać się z tym nagraniem: Windows Logon Process | Local User Account (momentami trudno jest zrozumieć akcent autora, ale dobrze opisane slajdy rekompensują niedogodności).

LSA (Local Security Authority) jest istotnym komponentem systemu Windows, który stanowi swojego rodzaju centrum bezpieczeństwa i odpowiada m.in. za:

  • weryfikację użytkowników na podstawie ich danych uwierzytelniających (najczęściej jest to po prostu nazwa konta i hasło);
  • wydawanie poświadczeń w postaci tokena dostępu (ang. access token) uwierzytelnionym użytkownikom (jest to coś w rodzaju pracowniczej karty dostępu z powyższego przykładu);
  • zapisywanie wszystkich istotnych zdarzeń związanych z bezpieczeństwem (jak choćby próby uwierzytelnienia) w dedykowanym logu (Windows Security Log).

Termin LSA bardziej odnosi się do konceptu, natomiast LSASS (Local Security Authority Subsystem Service) wskazuje na konkretny proces w systemie (lsass.exe), który odpowiada za zarządzanie dostępem. Dla uproszczenia, te dwa terminy były używane zamiennie podczas szkolenia, podobnie jak w tych notatkach.

Proces Winlogon (%SystemRoot%\System32\Winlogon.exe) jest odpowiedzialny za obsługę procesów logowania i wylogowania interaktywnego użytkownika, czyli m.in. za wyświetlenie ekranu logowania, przechwycenie wprowadzonych danych uwierzytelniających oraz wysłanie ich do LSA celem weryfikacji.

Przyjrzyjmy się teraz, jak z grubsza działa proces logowania użytkownika interaktywnego (do normalnej pracy z systemem) z użyciem nazwy konta/użytkownika i hasła. Poniższa lista skupia się na ogólnych aktywnościach tych dwóch komponentów (Winlogon oraz LSASS) i nie przedstawia kompletnej procedury, która jest dosyć mocno rozbudowana w systemie Windows.

  1. Inicjalizacja procesu Winlogon podczas inicjalizacji systemu. Na późniejszym etapie tej procedury, Winlogon rejestruje siebie jako tzw. logon process (zaufany komponent pośredniczący w operacji logowania) w LSA, za pomocą dedykowanej funkcji WinAPI LsaRegisterLogonProcess. W tym momencie Winlogon otrzymuje uchwyt (ang. handler) do procesu LSASS oraz ustanowiona jest komunikacja pomiędzy tymi dwoma procesami, za pośrednictwem protokołu ALPC (Advanced Local Procedure Call lub Asynchronous Local Inter-Process Communication), żeby mogły sprawnie wymieniać się informacjami.
  2. Kiedy rozpoczyna się procedura logowania użytkownika, Winlogon uruchamia proces LogonUI.exe odpowiedzialny za pobranie danych uwierzytelniających (ang. credentials) od użytkownika. Mówiąc krótko: pojawia się okno logowania.
  3. Po podaniu loginu i hasła, Winlogon za pomocą funkcji LsaLookupAuthenticationPackage, wybiera odpowiedni pakiet uwierzytelniający (ang. authentication package), czyli komponent zaimplementowany w bibliotece DLL, odpowiedzialny za właściwą weryfikację danych uwierzytelniających. System Windows oferuje różne metody uwierzytelniające (listę można znaleźć w rejestrze: HKLM\SYSTEM\CurrentControlSet\Control\Lsa), ale w przypadku lokalnego systemu jest to domyślnie MSV1_0 (%SystemRoot%\System32\Msv1_0.dll).
  4. Kiedy pakiet uwierzytelniający jest już wybrany, Winlogon, posługując się funkcją LsaLogonUser, wysyła prośbę o weryfikację użytkownika przez określony authentication package do LSA.
    1. W naszym przykładzie pakietem uwierzytelniającym jest MSV1_0, który bierze nazwę użytkownika oraz wylicza skrót hasła (używając kryptograficznej funkcji skrótu MD4) i próbuje na podstawie tych informacji pobrać z lokalnej bazy SAM (Security Account Manager) dane tegoż użytkownika. Wlicza się w to skrót hasła (celem porównania); grupy, do których należy użytkownik oraz wszelkie ograniczenia nałożone na to konto.
    2. LsaLogonUser pozwala również na określenie rodzaju logowania (LogonType), podając odpowiednią wartość zdefiniowaną w typie wyliczeniowym (enum) SECURITY_LOGON_TYPE. Nasz przykład opiera się na logowaniu interaktywnym (Interactive), ale do wyboru jest też m.in. typ Service (przeznaczony dla kont, w kontekście których działają usługi systemowe Windows) czy też Batch (np. dla zadań wykonywanych przez Windows Scheduler). Jest to istotna informacja, ponieważ LSA sprawdza również czy konto może zalogować się danym typem logowania na podstawie zdefiniowanych polityk bezpieczeństwa, o których powiemy sobie trochę później.
  5. Kiedy weryfikacja po stronie LSA przebiegnie pomyślnie (uwzględniając nie tylko dane logowania, ale również wszelkie ograniczenia nałożone przez zdefiniowane polityki bezpieczeństwa), generowane jest poświadczenie w postaci tokena dostępu (Access Token), który zostaje przekazany procesowi Winlogon.
  6. W kolejnym kroku Winlogon uruchamia specjalny proces Userinit.exe, którego zadaniem jest załadowaniu profilu uwierzytelnionego użytkownika, a następnie wystartowanie procesu explorer.exe, będącego rodzicem wszystkich procesów uruchamianych przez użytkownika. Kiedy to nastąpi, Userinit kończy swoje działanie.
    1. Gwoli ścisłości: proces userinit.exe jest domyślną wartością zdefiniowaną w rejestrze: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit. Podobnie jak explorer.exe jest wartością klucza HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell, więc teoretycznie można uruchomić inne procesy, jeśli podmienimy wartości wspomnianych kluczy. W przypadku procesu explorer.exe, tak naprawdę wystarczy utworzyć dodatkowy klucz ze ścieżką do pliku wykonywalnego: HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell – Winlogon najpierw sprawdza ten klucz i jeśli nie istnieje (jak to jest w domyśle), dopiero wtedy odczytuje wartość z klucza umieszczonego pod HKLM.
    2. Wspomniane procesy są uruchamiane z tożsamością określoną w otrzymanym tokenie. W domyśle, każdy proces potomny dziedziczy tożsamość po rodzicu. Mówiąc prościej: otrzymany token przechodzi na procesy potomne, które mają identyczne uprawnienia jak rodzic.

Podsumowując, kiedy użytkownik poprawnie się zaloguje za sprawą procesu LSASS i otrzyma token dostępu, który zawiera m.in. informacje o uprawnieniach użytkownika, jest on (token) ostatecznie przypisywany głównemu procesowi explorer.exe oraz wszystkim procesom potomnym. Innymi słowy, procesy działają z uprawnieniami zalogowanego użytkownika.

LSA może zostać poproszony o wygenerowanie nowego tokena dla danego procesu, w kontekście innego użytkownika – tak działa mechanizm Run As. W takim przypadku należy oczywiście podać prawidłowe dane uwierzytelniające docelowego użytkownika. W WinAPI do tego celu można użyć funkcji CreateProcessWithLogonW.

Rozszerzenia mechanizmu logowania

Okazuje się, że opisany wyżej proces logowania można rozszerzyć o dodatkowe funkcjonalności. Są to obszary mało udokumentowane, ale autor szkolenia (Grzegorz Tworek) zadał sobie trud zbadania tych mechanizmów i podzielił się nimi w trakcie szkolenia.

Generalnie, w pewnym momencie zarówno Winlogon jak i LSASS wykonują kilka czynności pobocznych na podstawie kodu zdefiniowanego w określonych bibliotekach DLL i nic nie stoi na przeszkodzie, żeby napisać i zarejestrować swoje biblioteki (zakładając oczywiście, że mamy do tego odpowiednie uprawnienia).

Winlogon & mpnotify.exe

W pewnym momencie procesu logowania, Winlogon sprawdza w rejestrze czy istnieje klucz HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\mpnotify. Jeśli tak, to będzie próbował uruchomić plik wykonywalny podany w wartości tego klucza. 

Kiedy takiego wpisu brakuje (jak to jest domyślnie), to uruchamiany jest wbudowany proces %systemroot%\System32\mpnotify.exe, odpowiedzialny za dostarczanie w czasie rzeczywistym powiadomień związanych z bezpieczeństwem.

Kiedy proces mpnotify wystartuje, odczytuje ze swojej gałęzi rejestru biblioteki DLL, które powinny być załadowane w jego kontekście (tzw. notification packages). Idea stojąca za tym mechanizmem polega na powiadomieniu określonych komponentów systemowych Windows o zdarzeniach związanych z uwierzytelnieniem. Następnie otwierany jest kanał komunikacji LRPC (Lightweight Remote Procedure Call) z Winlogon, który wysyła nim m.in. dane uwierzytelniające do mpnotify, a ten przekazuje później dane do załadowanych DLL-ek.

Gdyby udało nam się podstawić naszą, spreparowaną bibliotekę DLL, moglibyśmy przechwycić dane przesłane przez Winlogon, w tym login i hasło użytkownika, który właśnie próbuje się uwierzytelnić.

Załóżmy więc, że chcemy przejąć hasło innego administratora w systemie. Moglibyśmy napisać własny proces mpnotify i następnie umieścić ścieżkę do naszej binarki w rejestrze, ale jest prostszy sposób. Domyślny proces systemowy mpnotify.exe również przechowuje w rejestrze informacje o DLL-kach, którym przekazuje dane otrzymane od Winlogon. Można je znaleźć w kluczu HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order\ProviderOrder. Wystarczy więc, że napiszemy własną bibliotekę DLL udającą Network Provider i dodamy ją do tej listy w rejestrze.

Cały proces został przedstawiony na tym nagraniu (również od Grzegorza): How winlogon.exe shares the cleartext password with custom DLLs. Przykładowy kod źródłowy znajduje się w repozytorium: gtworek/PSBits/PasswordStealing/NPPSpy. Znajdziemy tutaj kod biblioteki, która zapisuje przechwycone hasło do pliku tekstowego oraz skrypt PowerShell dodający nasz fake’owy network provider do rejestru. Aby kod zadziałał musimy ponownie wywołać proces winlogon.exe, więc powinniśmy się jeszcze raz zalogować.

Opisany wyżej mechanizm, uwarunkowany wsteczną kompatybilnością, może stanowić pewnego rodzaju lukę w bezpieczeństwie, choć należy pamiętać, że do jego wykorzystania potrzebne są uprawnienia administracyjne. Na wszelki wypadek możemy raz na jakiś czas skontrolować listę providerów sieciowych w rejestrze za pomocą skryptu PS (również z powyższego repozytorium): Get-NetworkProviders.ps1.

LSASS & password filters

LASS jest procesem nieźle zaprojektowanym i dobrze zaimplementowanym, więc trudniej jest go zaatakować. Okazuje się jednak, że można wykorzystać podobny myk, jak ten opisany wyżej w przypadku Winlogon i podstawić swoją bibliotekę do filtrowania haseł (Password Filter).

Wspomniane filtry to nic innego jak biblioteki DLL, wywoływane przez LSA w chwili, gdy użytkownik chce zmienić hasło. Ich zadaniem jest wymuszenie odpowiedniej polityki (np. hasło nie może być krótsze niż 12 znaków) oraz powiadomienie o fakcie zmiany hasła.

Kod biblioteki DLL udającej taki filtr hasła można znaleźć tutaj: gtworek/PSBits/PasswordStealing/PSPY.c. Przechwytuje ona nowe hasło użytkownika, otrzymane od LSA i zapisuje je w pliku tekstowym. Następnie, zawsze zwraca informację, że nowe hasło spełnia wymagane kryteria.

Proces instalacji jest bardzo prosty: wystarczy umieścić skompilowaną DLL-kę w katalogu %systemroot%\System32; dodać (nie nadpisać) nazwę pliku do klucza rejestru HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages oraz upewnić się, że w ustawieniach systemowych jest włączona opcja wymuszania odpowiedniej złożoności hasła. Cały proces jest opisany w dokumentacji: Installing and Registering a Password Filter DLL.

Zostało tylko poczekać, aż jakiś użytkownik systemu będzie chciał zmienić swoje hasło. Oczywiście należy pamiętać, że możemy to traktować jako lukę w zabezpieczeniach, ale nie damy rady jej wykorzystać jeśli nie posiadamy uprawnień administracyjnych.

Access Token

Token dostępu (ang. access token) jest specjalną strukturą danych, która zawiera informacje o tzw. kontekście bezpieczeństwa (ang. security context) procesu lub wątku. Informacje w nim zawarte to m.in. tożsamość konta użytkownika, jego przynależność do określonych grup oraz uprawnienia jakie posiada.

Token jest wydawany przez LSA w chwili, gdy użytkownik zostanie poprawnie uwierzytelniony. Każdy proces uruchomiony w imieniu użytkownika posiada kopię tokena wydanego temu użytkownikowi. Więcej na temat tokenów można znaleźć w artykule: Introduction to Windows tokens for security practitioners | Elastic Blog.

Wartość tokena można podejrzeć na kilka sposobów:

  • Polecenie CMD whoami /all – wyświetla najważniejsze informacje z tokena, czyli identyfikator SID (Security Identifier) użytkownika; przynależność do grup oraz przywileje (ang. privileges). Tak naprawdę jest to token przypisany do procesu cmd.exe, ale jak zostało już wspomniane, został on wcześniej wydany uwierzytelnionemu użytkownikowi przez LSA – dzięki temu proces ma tylko takie uprawnienia jak użytkownik, który go uruchomił.
  • WinDbg – podpinamy (ang. attach) się pod wybrany proces i wydajemy polecenie !token.
  • Process Hacker – rozwijamy menu kontekstowe dla wybranego procesu z listy (prawy przycisk myszy) -> Properties -> zakładka Token. Po kliknięciu przycisku Advanced, ujrzymy bardziej szczegółowe informacje.

Well-known SIDs

Przeglądając listę procesów w różnych narzędziach, jak choćby wspomniany wcześniej Process Hacker, znajdziemy informacje o użytkownikach tych procesów. To co może rzucić się w oczy, to fakt, że część tych nazw nie wskazuje na fizycznie istniejące konto bądź domenę w systemie – na przykład użytkownik NT AUTHORITY\LocalService z zadziwiająco krótkim SID-em: S-1-5-19 bądź grupa NT AUTHORITY\Local account z SID-em S-1-5-113.

Jest to przykład jednego z tzw. Well-known SIDs, czyli globalnych identyfikatorów wskazujących na predefiniowanych/wbudowanych (generycznych) użytkowników oraz grupy. Oznacza to, że na różnych maszynach znajdziemy identyczne wartości SID dla wspomnianych użytkowników bądź grup. Pozwala to na zachowanie pewnych standardów w mechanizmach zabezpieczających, stosowanych w systemach z rodziny Windows.

Pożyczanie tokena

W systemie Windows nie istnieje procedura aktualizacji tokenów, po wydaniu przez LSA. Oznacza to, że jeśli zajdzie taka potrzeba, to należy zalogować się ponownie, żeby otrzymać nowy, zaktualizowany token. Jeśli jednak mamy uprawnienia administracyjne (bądź nasz użytkownik posiada przywilej SeDebugPrivilege), możemy sobie pożyczyć (lub mówiąc kolokwialnie podkraść) token od innego procesu.

Można to zrobić np. za pomocą wspomnianej wyżej aplikacji Process Hacker. Załóżmy, że potrzebujemy na chwilę uruchomić proces z uprawnieniami konta NT AUTHORITY\SYSTEM. Znajdujemy na liście procesów taki, który działa w kontekście wspomnianego konta i z menu kontekstowego (prawy przycisk myszy) wybieramy kolejno: Miscellaneous ->  Run as this user… i wybieramy proces, który chcemy uruchomić z nowymi uprawnieniami.

Inny sposób to skorzystanie z narzędzia PsExec: psexec -i -d -s cmd.exe. Polecenie uruchamia nowy proces cmd.exe w trybie interaktywnym (-i); bez czekania na jego zakończenie (-d) oraz w kontekście konta systemowego, z rozszerzonymi uprawnieniami (-s). Oczywiście sam PsExec powinien być uruchomiony z prawami administratora, żeby udało się uzyskać token konta systemowego.

Jeśli kogoś interesuje, jak mechanizm przejmowania tokena działa pod spodem, może zapoznać się z tym artykułem: APT techniques: Access Token manipulation. Token theft. Simple C++ example. – cocomelonc.

Zabawy z tokenem

Zdając sobie sprawę z istnienia wspomnianych wcześniej well-known SIDs oraz wirtualnych nazw grup i użytkowników, dla których nie ma fizycznego konta w systemie, niektórzy mogą się zastanawiać, czy da się w takim razie wygenerować token zawierający dowolną, zmyśloną nazwę domeny i użytkownika. Autor szkolenia (Grzegorz) nie tylko się zastanowił, ale też i udowodnił, że jest taka możliwość.

Posiadając odpowiednie uprawnienia, możliwe jest wygenerowanie tokena na życzenie, podobnie jak to robi Winlogon. Demonstruje to przykładowy kod RunAsVA.c, który tworzy nową, zmyśloną tożsamość (w tym przypadku nazwa domeny i użytkownika to po prostu emotikonki) wraz z własnymi wartościami SID, a następnie prosi LSA o wygenerowanie tokena dostępu dla tego użytkownika.

Generowanie tokena odbywa się za sprawą funkcji LogonUserExExW. Ponieważ zwrócony token jest typu Impersonation (w uproszczeniu: do użycia jedynie przez wątki), należy skonwertować go na typ Primary (podstawowy typ wydawany użytkownikom i przypisywany procesom) poprzez jego duplikację: DuplicateTokenEx. Zduplikowany token typu Primary (zawierający nazwę zmyślonego konta)  może być teraz użyty do utworzenia nowego procesu.

Żeby wykonać powyższą sztuczkę, potrzebne jest uprawnienie SeTcbPrivilege, które posiada konto LocalSystem, ale wiemy już przecież jak pożyczyć sobie odpowiedni token za pomocą narzędzi Process Hacker lub PsExec.

W tym samym repozytorium znajdziemy podobny przykład z wygenerowaniem tokena dla własnej, wirtualnej nazwy konta, ale tym razem z uprawnieniami TrustedInstaller.

Token dostępu posiada również atrybuty (Process Hacker: R-click -> Properties -> Token -> Advanced -> Attributes), do których można wrzucić dodatkowe informacje. Od jakiegoś czasu system Windows dodaje tam wpisy z identyfikatorem procesu (TSA://ProcUnique), ale trudno powiedzieć czy jest to gdzieś rzeczywiście wykorzystywane.

Okazuje się, że my również możemy coś tam dodać, jeśli czujemy taką potrzebę. Autor szkolenia nie udostępnił w tym przypadku kodu źródłowego, ale uchyla rąbka tajemnicy, jak tego dokonać, na Twitterze.

Privileges

Przywileje/uprawnienia (ang. privileges) określają pewne działania, które można wykonać w danym systemie operacyjnym. W zależności od konta będziemy mieli inny zestaw przywilejów: zwykły użytkownik posiada mniej uprawnień niż administrator, a ten z kolei na ma tych, które posiada konto LocalSystem.

Poza tym zestaw uprawnień może znacząco się różnić w przypadku różnych obiektów systemowych czy nawet samych systemów. Przykładowo, zwykły użytkownik w Windows 11 posiada uprawnienie do wyłączenia komputera (SeShutdownPrivilege), ale nie ma takiej możliwości (w domyśle) w systemach z rodziny Windows Server.

Część z tych przywilejów można wykorzystać w ciekawych celach. W repozytorium Priv2Admin znajdziemy listę popularnych uprawnień oraz informacje o tym, w jaki sposób dane uprawnienie ma wpływ na działania systemu i czy stanowi potencjalne zagrożenie. Kolumna Impact wskazuje na to, co można uzyskać kiedy posiadamy wybrany przywilej jako zwykły użytkownik. Dodatkowo, w tabeli znajdziemy informacje o potencjalnych sposobach wykorzystania danego przywileju.

Ciekawsze przykłady:

  • SeChangeNotifyPrivilege – zapewnia dostęp do pliku, nawet jeśli użytkownik nie ma uprawnień do czytania zawartości wszystkich katalogów w ścieżce do tego pliku. Teoretycznie jest niegroźny, ale poprzez nieumiejętne grzebanie w nim, można sobie zepsuć proces bootowania systemu Windows.
  • SeBackupPrivilege – przywilej pozwalający na wykonywanie kopii zapasowych (ang. backup). Potencjalnie groźny, ponieważ użytkownik z tym uprawnieniem będzie miał dostęp do plików (a dokładnie do czytania ich zawartości), do których teoretycznie nie ma uprawnień podczas zwykłej pracy w systemie. Przypisany standardowo do grupy Backup Operators, więc należy również z rozwagą przypisywać użytkowników do tej grupy.
  • SeRestorePrivilege – jak sama nazwa wskazuje, jest to przywilej do przywracania kopii zapasowej. Teoretycznie może stanowić poważniejszy problem niż wspomniany wcześniej SeBackupPrivilege, ponieważ pozwala na modyfikowanie zawartości plików, do których użytkownik normalnie nie ma dostępu. Przykładowo, posiadając to uprawnienie można podmienić systemowe pliki binarne, biblioteki DLL czy pliki wykonywalne usług systemowych (windows services).
    • Prosty przykład nadużycia: podmiana pliku utilman.exe (Utility Manager odpowiadający za funkcje ułatwienia dostępu) na cmd.exe. Następnie, na ekranie logowania, należy włączyć funkcję Ease of Access (klikając ikonę lub za pomocą kombinacji klawiszy Win+U) – zamiast domyślnego procesu utilman.exe uruchomi się wiersz poleceń.
    • Szersze wyjaśnienie: Bypassing ACLs with SeRestore privilege. And very simple User to LocalSystem elevation.
  • SeSystemtimePrivilege – to nie literówka – time rzeczywiście jest z małej litery. Uprawnienie pozwalające na zmianę czasu systemowego. W zasadzie nic groźnego, ale może posłużyć do zacierania śladów, np. wpisy w logach systemowych będą wskazywały na inny czas, niż kiedy złośliwe aktywności rzeczywiście miały miejsce.
  • SeDebugPrivilege – wspomniane już wcześniej uprawnienie, umożliwiające debugowanie procesów należących do innych użytkowników. Zezwala na pożyczenie tokena od innego procesu, a tym samym uzyskanie praw właściciela tego procesu – dlatego zwykli użytkownicy nie powinni posiadać tego przywileju.
  • SeManageVolumePrivilege – wg opisu: Perform volume maintenance tasks. Z dokumentacji dowiadujemy się, że jest to prawo do zarządzania woluminami lub dyskami, czyli wykonywania takich operacji jak np. defragmentacja; tworzenie/usuwania woluminów czy szeroko rozumiane czyszczenie (Disk Cleanup).
    • Szczegółowe wyjaśnienie, w jaki sposób można wykorzystać to uprawnienie: Local Privilege Escalation with SeManageVolumePrivilege. Grzegorz pokazuje na filmie, w jaki sposób za pomocą nieudokumentowanego komunikatu FSCTL (File System Control), wysłanego do dysku C:\, można zmienić prawa dostępu dla całego katalogu. 
    • FSCTL_SD_GLOBAL_CHANGE.c – kod demonstrujący w jaki sposób wysłać wspomniany komunikat FSCTL i za jego pomocą zmodyfikować security descriptor dla całego katalogu C:\.

Przywileje opisane powyżej są lub powinny być w domyśle nadane jedynie zaufanym użytkownikom, czyli głównie administratorom. Żeby atak privilege escalation był skuteczny, te uprawnienia musiałyby być nierozważnie przypisane zwykłemu użytkownikowi.

Jeśli konto ma już przypisane uprawnienia, to bez problemu można je włączać (ang. enable) i wyłączać (ang. disable). Z punktu widzenia bezpieczeństwa nie ma to znaczenia, ponieważ posiadacz danego przywileju może sobie nim swobodnie zarządzać. Tutaj bardziej chodzi o dodatkową ochronę przed niezamierzonym błędem i świadome korzystanie z wybranych uprawnień (szczególnie przez programistów).

Windows posiada wbudowane narzędzie secpol.msc do zarządzania uprawnieniami z użyciem interfejsu GUI. W tej aplikacji punktem wyjściowym są przywileje, a nie konta użytkowników (tzn. do wylistowanych przywilejów przypisujemy konta, a nie odwrotnie).

W secpol.msc można zarządzać nie tylko przywilejami, w dosłownym tego słow znaczaniu, ale też politykami dostępu (ang. policies). Na przykład, pozycja Security Settings -> Local Policies -> User Rights Assignment -> Access this computer from the network nie jest stricte uprawnieniem, a polityką dostępu, ponieważ jest to ustawienie weryfikowane przez LSA, zanim jeszcze zostanie wygenerowany token dostępu z wpisanymi przywilejami.

Wszystkie uprawnienia wraz z ich właścicielami są zapisane w bazie SAM (Security Account Manager), będącej częścią rejestru systemowego i przechowywanej w pliku %SystemRoot%\System32\Config\SAM. Dostęp do tego pliku jest mocno utrudniony, ponieważ jest on stale używany przez proces LSASS.

Jeśli jednak jesteśmy wystarczająco zdeterminowani, możemy zmodyfikować bazę SAM bezpośrednio, przeprowadzając tzw. atak offline. Dokładniejszy opis techniki, wraz z przykładami, jest dostępny w repozytorium OfflineSAM. Przykłady pokazują w jaki sposób nadać uprawnienia administratora wszystkim kontom z bazy SAM oraz jak można dodać nowego użytkownika z uprawnieniami admina.

Security Descriptor (SD)

SDDL

Praktycznie każdy obiekt w systemie Windows posiada tzw. security descriptor, który jest strukturą binarną przechowującą informacje związane z bezpieczeństwem danego obiektu (głównie te związane z dostępem). Przykład takiej struktury (w postaci binarnej) znajdziemy rejestrze, kiedy sprawdzimy wpis dla usługi gpsvc (Group Policy Client): HKLM\SYSTEM\CurrentControlSet\Services\gpsvc\Security\Security.

Na szczęście deskryptor posiada również postać tekstową, która jest bardziej zrozumiała dla człowieka, a konkretnie jest opisany specjalnym językiem SDDL (Security Descriptor Definition Language). 

Deskryptor SDDL dla naszej przykładowej usługi możemy sprawdzić wydając polecenie w CMD: sc.exe sdshow gpsvc, na razie jako zwykły użytkownik. Wynik może wyglądać następująco:

D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCLCSWLOCRRC;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)

Dosyć enigmatyczne na pierwszy rzut oka, ale spróbujmy to rozpracować:

  • D: – Wskazuje na DACL (Discretionary Access Control List), czyli informacje o tym, co i komu wolno zrobić z danym obiektem.
  • Każdy z tych nawiasów reprezentuje pojedynczy wpis ACE (Access Control Entry) i zawiera przywileje dla określonej grupy. Ponieważ mamy do czynienia z predefiniowanymi grupami, podane są znaczące skróty (w przeciwnym razie byłyby to identyfikatory SID):
    • SY = LocalSystem.
    • BA = Built-in Administrators.
    • IU = Authenticated Users.
    • SU = Service.
  • A = Allow Access. Oznacza, że dana grupa ma przyznane uprawnienia, reprezentowane przez ciąg znaków widoczny wewnątrz nawiasów. Nie będziemy tutaj opisywać poszczególnych bitów uprawnień, bo można je sobie z łatwością sprawdzić w oficjalnej dokumentacji: ACE Strings – Win32 apps | Microsoft Learn lub poprosić ChatGPT, żeby nam to odszyfrował – pamiętajmy tylko, żeby nie wklejać tam wrażliwych informacji.

W zależności od uprawnień użytkownika, deskryptor może zawierać trochę inne informacje. Na przykład, wykonując powyższe polecenie jako administrator (lub ktoś z przywilejem SaSecurityPrivilege), dostaniemy trochę bardziej rozbudowany string:

D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCLCSWLOCRRC;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;SAFA;DCSDWDWO;;;WD)

Zwróćmy uwagę na dodatkowy wpis ACE rozpoczynający się od S:, czyli SACL (System Access Control List), który określa jakiego typu czynności czy próby dostępu powinny być monitorowane i logowane (innymi słowy: w jaki sposób patrzymy użytkownikom systemu na ręce).

Podany wyżej przykład pokazuje SD dla obiektu będącego usługą systemową Windows, ale inne obiekty (np. pliki czy klucze rejestru) prawdopodobnie będą posiadały deskryptory bezpieczeństwa innego rodzaju. To od typu obiektu zależy co można z nim zrobić i nawet jeśli deskryptory różnych obiektów mają podobne bity, to mogą one mieć zupełnie inne znaczenie.

Listę obiektów systemowych zarządzanych przez Object Manager da się sprawdzić za pomocą narzędzia WinObj. Przy okazji warto wspomnieć, że w systemie Windows istnieje pewna klasa obiektów, które nie są zarządzane przez Object Manager, a przez inne (dedykowane) byty systemowe. Na przykład:

  • Windows services – zarządzanie przez SCM (Service Control Manager).
  • Active Directory.
  • Uprawnienia do udziałów sieciowych.

Deskryptory plików

Deskryptory plików (podobnie jak deskryptory kluczy rejestru) można sprawdzić i zmodyfikować za pomocą systemowego UI, wchodząc we właściwości pliku i zakładkę Security. Jednakże ten interfejs jest bardzo mocno ograniczony i nie nadaje się do poważniejszych rzeczy, dlatego lepiej zajrzeć trochę głębiej, klikając przycisk Advanced

Tutaj widzimy szczegółowe informacje o poszczególnych uprawnieniach oraz skąd zostały odziedziczone. Poza tym widzimy je w prawidłowej kolejności. Wybierając wpis (permission entry) i wchodząc w jego szczegóły, widzimy wizualną reprezentację tego, co prezentuje string SDDL.

Czasami trudno jest zweryfikować jakie ostateczne uprawnienia ma dany użytkownik, kiedy ten należy do wielu grup – wtedy część odziedziczonych uprawnień jest niewidoczna na pierwszy rzut oka. Zamiast jednak skakać po grupach, można skorzystać z zakładki Effective Access (dostępnej w zaawansowanych opcjach omawianego właśnie UI) i zobaczyć jak wyglądają efektywne (ostateczne) uprawnienia wybranego konta.

Inne narzędzia:

  • icacls.exe – narzędzie systemowe (z interfejsem CLI) pokazujące uprawnienia do wybranego pliku i umożliwiające ich modyfikacje. Znaczenie poszczególnych kodów uprawnień jest dostępne w dokumentacji programu.
    • Kiedy konto posiada wszystkie uprawnienia, aplikacja pokazuje po prostu wartość (F), co oznacza pełny dostęp (Full Access). Jeśli interesują nas poszczególne uprawnienia, wystarczy uruchomić program z flagą /dbg.
    • Problem z kodami poszczególnych przywilejów w tym narzędziu polega na tym, że nie są to te same oznaczenia co w SDDL. Jeśli jednak poprosimy o zapisanie wyników do pliku za pomocą opcji /save, to wtedy otrzymamy SDDL – dziwne, ale działa. Warto przy okazji wspomnieć, że niektóre bity wyeksportowane do SDDL mogą mieć wartość heksadecymalną zamiast opisowej.
  • (Get-Acl <PATH>).Sddl – wyświetla deskryptor w postaci SDDL w PowerShell.
  • Do sprawdzenia efektywnego dostępu wybranych obiektów systemowych (nie tylko plików), można wykorzystać narzędzie AccessChk.

Narzędzia pokazujące efektywny dostęp do pliku często są w stanie wskazać problemy z uprawnieniami, ponieważ okazuje się, że system Windows daje takie możliwości ich konfiguracji, że czasami można nieźle namieszać (niechcący bądź intencjonalnie). Dobrze zostało to opisane w artykule Bezpieczeństwo Windows. Autoryzacja od środka, czyli czego prawdopodobnie nie wiesz o ACL.

Sytuacją, która może wydawać się trochę dziwna, jest brak dostępu do jednego z katalogów w całej ścieżce do pliku. Załóżmy, że użytkownik chce się dostać do pliku C:\A\B\C\file.txt – ma uprawnienie do odczytu zawartości docelowego pliku oraz wszystkich katalogów z podanej ścieżki, oprócz katalogu B. Mimo to, użytkownik będzie w stanie odczytać plik file.txt i jest to możliwe dzięki wymienionemu już wcześniej przywilejowi SeChangeNotifyPrivilege (Bypass traverse checking).
Jeśli ktoś jest zainteresowany tematem warunkowych ACL (np. możliwość odczytania zawartości pliku jedynie przez wybrane aplikacje), to może zapoznać się z tą prezentacją: How to use conditional ACEs to get a file one app can open, while another one cannot.

~Łukasz Mieczkowski

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. Arek

    łał :o

    Odpowiedz
  2. Jesiotr

    To może jeszcze poproszę parę słów o CredentialUIBroker.exe i jego odsłonach „OK” oraz „user/pass”. Kiedy się pojawia, jak zrobić, żby się nie pojawiał itd :-)

    Odpowiedz
  3. R

    Czyli weryfikacja zainstalowana na jakimś urządzeniu jest w miarę bezpieczna? Ostatnio co prawda FB zmienił zabezpieczenia z lepszych na gorsze (wymagają włączenia kodów sms) ale cały portal jest dobrym źródłem informacji dla hakerów i pełen syfu.

    Odpowiedz
  4. WalterBlack

    Szacun i wielkie dzięki za ten artykuł!

    Odpowiedz

Odpowiedz