Eskalacja uprawnień w domenie Windows – CVE-2021-42287 + CVE-2021-42278

Ostatnio nie mogłem sobie pozwolić na wygospodarowanie czasu na pisanie postów tutaj, ale tym wpisem postaram się przełamać tą niemoc. Tym razem zapraszam na omówienie ataku na protokół Kerberos, który umożliwia eskalację uprawnień w domenie Windows.

sAMAcountName czyli dolary są istotne

Atrybut „samaccountname” jest jednym z wielu atrybutów opisujących obiekt w domenie Active Directory. Drugim niezwykle ważnym atrybutem jest „userprincipalname”, jednak ten drugi dotyczy tylko użytkowników, pierwszy natomiast użytkowników i komputerów. Skupmy się więc na chwilę na „samaccountname„. Dlaczego samaccountname jest taki istotny? Ponieważ protokół Kerberos wykorzystuje właśnie ten atrybut do uwierzytelnienia obiektu w domenie. Charakterystyczne dla kont komputerów w domenie Windows jest to, że posiadają one znaczek dolara ($) na końcu swojej nazwy w omawianym atrybucie.

Problem podatności CVE-2021-42278 polega na tym, że żadne mechanizmy bezpieczeństwa nie weryfikują obecności dolara podczas procesu uwierzytelnienia. Innymi słowy, każdy użytkownik posiadający write-access do konta komputera, może usunąć znaczek dolara z atrybutu samaccuountname.

S4U2self czyli otrzymanie service ticketu w imieniu innego użytkownika

W domenie Active Directory istnieje pojęcie delegacji. Nie chciałbym się teraz zagłębiać w ten temat, więc na potrzeby tego wpisu wyjaśnię tylko, że użytkownicy lub komputery, które mają skonfigurowaną delegację mogą prosić o tickety dla siebie, ale w imieniu innych użytkowników. Preces ten jest znany jako S4U2self – czyli service for user to self. Innymi słowy, użytkownik uprawniony do delegacji może uwierzytelniać się do różnych serwisów w imieniu innych użytkowników domeny. Więcej o delegacji możecie przeczytać na oficjalnych stronach Microsoftu, a o tym jak można wykorzystywać ten feature w kontekście bezpieczeństwa IT możecie przeczytać m.in. tu. Należy jeszcze dodać, że delegacja może być bezwarunkowa (unconstrained), lub ograniczona tylko do określonych serwisów (constrained). Cechą charakterystyczną Kontrolerów Domeny jest to, że mają one zawsze włączoną nieograniczoną delegację.

Na powyższym obrazku widzicie różnicę w ustawieniach dla kontrolera domeny (DC1) po lewej stronie oraz dla zwykłej stacji (WK03) po prawej stronie. Jeżeli macie włączoną delegację to można w bardzo prosty sposób poprosić o Service Ticket (ST) do danego serwisu w imieniu dowolnego użytkownika np. za pomocą programu Rubeus lub GetST.py z pakietu Impacket. Zarówno Rubeus jak i GetST.py mogą poprosić o nowy bilet TGT (który jest wymagany do prośby o ST), jak i użyć już istniejącego biletu TGT.
Na czym polega podatność CVE-2021-42287? Podczas prośby o Service Ticket, w przypadku nieodnalezienia w domenie podmiotu wnioskującego o ST (uwzględnionego w TGT), KDC (Key Distribution Center) przeszukuje domenę w poszukiwaniu konta z TGT, ale z dolarem na końcu.

Łączymy kropki, czyli atak w praktyce

Może niektórym zaświeciła się już lampka i wiedzą jak połączyć dwie opisane wyżej podatności. Przede wszystkim potrzebujemy komputera, do którego atrybutów będziemy mieli write-access. Będąc nieuprzywilejowanym użytkownikiem w domenie możemy zrobić to tylko i wyłącznie dodając nowy komputer do domeny. Najłatwiej możemy to zrobić z wykorzystaniem skryptu PowerMad. Wszystkie czynności będę wykonywać z poziomu nieuprzywilejowanego użytkownika „l.user”.

Okej, wygląda na to, że udało nam się stworzyć tymczasowy komputer. Kontroler domeny w mojej testowej domenie nazywa się „DC01”, a jego samaccountname to „DC01$”. Spróbujmy więc zamienić samaccountname nowoutworzonej stacji na „DC01”.

Niestety, ale nie jesteśmy w stanie zmienić tego atrybutu, mimo posiadanych uprawnień. Okazuje się, że podczas zmiany atrybutu „samaccountname”, system stara się automatycznie zmienić atrybut „serviceprincipalname” na nowy, korespondujący z „samaccountname”. Niestety, ale SPN o nazwie np. „HOST/dc01.how2hax.pl” już istnieje, więc nie może w ramach jednej domeny istnieć drugi o takiej samej nazwie. Na szczęście i na to jest obejście – system nie będzie próbować zaktualizować SPNów, jeżeli ich nie będzie! Spróbujmy więc najpierw wyczyśćić atrybut „serviceprincipalname”, a dopiero później zaktualizować atrybut „samaccountname”.

Udało się! Mamy stację o nazwie „tempworkstation”, ale jej „samaccountname” to DC01. Spróbujmy teraz poprosić o bilet TGT w imieniu tej stacji. Użyjemy do tego program Rubeus. Na Linuxie wykorzystalibyśmy „GetTGT.py” z pakietu Impacket.

Okej. Mamy bilet TGT, który uprawnia nas do wystąpienia o Service Ticket. Zerknijmy na użytkownika na jakiego został wystawiony TGT – jest to „DC01” i przypomnijmy sobie teraz poprzedni akapit – co stanie się w przypadku wykorzystania tego ticketu, gdy użytkownik (komputer) o nazwie „DC01” nie zostanie odnaleziony? KDC poszuka w bazie AD podmiotu o nazwie „DC01$” i zaszyfruje Service Ticket (a w zasadzie jego fragment – EncTicketPart – patrz dokumentacja) kluczem kontrolera domeny. Sprawdźmy czy to się uda.

Analizując powyższy zrzut ekanu widzimy, że udało nam się otrzymać bilet ST do usługi LDAP na kontrolerze domeny. Prosze zwrócić uwagę na przełącznik „impersonateuser”. Pamiętamy, że w delegacji chodzi o to, że możemy uwierzytelniać się do określonej usługi w imieniu innego użytkownika. W powyższym przykładzie chcemy przedstawić się usłudze LDAP na kontrolerze domeny jako użytkownik „p.kowalczyk”. Dlaczego? Ponieważ jest on Administratorem Domeny. Sprawdźmy jeszcze czy bilet został prawidłowo załadowany do pamięci.

Zanim spróbujemy go wykorzystać sprawdźmy czy bilet został zaszyfrowany kluczem naszej testowej maszyny (testworkstation). W tym celu sprawdźmy jaki jest skrót hasła stacji tempworkstation$

i sprawdźmy czy tym kluczem możemy rozszyfrować otrzymany Service Ticket. Teoretycznie ten użytkownik (komputer) zawnioskował o Ticket, więc bilet powinien być zaszyfrowany jego hasłem.

Z powyższego obrazka wynika, że ticket nie został zaszyfrowany kluczem stacji „tempworkstation”. Czyim więc kluczem został zaszyfrowany? Mam nadzieję, że domyślamy się, że KDC zaszyfrował ten ticket kluczem DC. Sprawdźmy to. Na początek znajdźmy właściwy klucz Kontrolera Domeny:

A następnie zweryfikujmy, czy możemy odszyfrować nasz ticket kluczem Kontrolera Domeny.

Świetnie! KDC zaszyfrował ticket kluczem Kontrolera Domeny! Spróbujmy więc wykorzystać nasz załadowany do pamięci ticket w celu wykonania ataku DCSync i odczytania informacji o użytkowniku krbtgt.

Widzimy, że atak został się powiódł. Oznacza to, że KDC wydał Service Ticket do usługi LDAP na kontrolerze domeny i podpisał ten ticket kluczem Kontrolera Domeny, dzięki czemu możemy wykorzystać ten ticket do uwierzytelnienia się do LDAPa. Jedną z metod wykorzystania tego serwisu jest właśnie wykonanie ataku „DCSync”.

TL:DR

Powyższe czynności można z powodzeniem zrealizować za pomocą Kaliego Linuxa oraz pakietu Impacket. Rzecz jasna wymogiem jest mniej lub bardziej bezpośredni dostęp do kontrolera domeny. Na szczęście wszystko to co opisano powyżej zostało już zautomatyzowane i możecie sami w prosty sposób przeprowadzić taki atak na testowym środowisku z wykorzystaniem np. tego projektu. Efekt powinien być podobny do tego co poniżej:

Podsumowanie

Microsoft załatał wyżej opisane podatności w łatce KB5008602. Jaki jest jednak najprostszy sposób aby zapobiec takim atakom? Proszę zwrócić uwagę na początek poprzedniego akapitu. Cały atak rozpoczął się od dodania stacji roboczej do domeny przez nieuprzywilejowanego użytkownika. Bardzo często możliwość dodania nowych stacji do domeny niesie za sobą różne negatywne konsekwencje związane z bezpieczeństwem, a powyższy przykład tylko potwierdza tę tezę. O ile to możliwe, należy ograniczać takie możliwości użytkownikom domeny.

Źródła:

eXploit – CVE-2021-42287/CVE-2021-42278 Weaponisation

sAMAccountName spoofing – The Hacker Recipes

Dodaj komentarz