Active Directory Forest Trusts del 1 – Hur fungerar SID-filtrering?

Det här är det första inlägget i en serie om Active Directory-förtroenden mellan olika skogar. Det kommer att förklara vad exakt Forest trusts är och hur de skyddas med SID-filtrering. Om du är nybörjare på Active Directory-förtroenden rekommenderar jag att du börjar med att läsa harmj0ys djupgående guide om dem. Efter att ha läst hans (utmärkta) inlägg hade jag många frågor om hur detta faktiskt fungerar under huven och hur förtroenden inom samma AD-skog jämförs med förtroenden mellan olika skogar. Den här serien av bloggar är både min resa och min dokumentation om hur jag undersökte det här ämnet och hur jag förstår det nu. Gör dig redo för en djupdykning i förtroenden, Kerberos, golden tickets, mimikatz och impacket!

Detta inlägg kommer att diskutera förtroenden mellan olika skogar. En skog är en samling av en eller flera domäner som ingår i ett eller flera domänträd. I organisationer med endast en domän utgör den domänen också hela skogen. I Microsofts dokumentation kallas förtroenden ofta antingen interforest trusts (förtroenden mellan två olika skogar) eller intraforest trusts (förtroenden mellan domäner i samma skog). Eftersom dessa ord alltid förvirrar mig kommer jag i det här inlägget att referera till dem som antingen förtroenden inom skogen eller förtroenden mellan skogar (eller mellan skogar). Det är också viktigt att komma ihåg att även om vi diskuterar förtroenden mellan två skogar definieras förtroenden alltid mellan domäner. Skogsförtroenden kan endast skapas mellan två rotdomäner i olika skogar, så alla omnämnanden i det här inlägget av ett skogsförtroende är förtroendet mellan två olika rotdomäner.

Inom en enskild skog litar alla domäner på varandra och du kan eskalera från en komprometterad domän till alla de andra, vilket förklaras i Sean Metcalfs forskning om domänförtroenden. För att upprepa: En Active Directory-domän är inte en säkerhetsgräns, det är en Active Directory-skog.

Vi kommer också att tala om säkerhetsidentifierare (SID). En SID är något som unikt identifierar en säkerhetsprincipal, t.ex. en användare, grupp eller domän. En av domänerna i testskogarna har SID S-1-5-21-3286968501-24975625-1618430583. Den välkända gruppen Domain Admins, som har ID 512, har ett SID som består av domänens SID och ID (som kallas RID i AD-terminologi), vilket ger den SID S-1-5-21-3286968501-24975625-1618430583-512 i den här domänen.

Installationen

Installationen innehåller 3 Active Directory-skogar: A, B och C. Både skog A och skog C har ett tvåvägs transitivt skogsförtroende (transitive Forest trust) med skog B (vi kommer senare att förklara exakt vad detta innebär). Skog A och skog C har inget förtroende mellan varandra. Skog A är den enda skogen med två domäner: forest-a.local och sub.forest-a.local. Eftersom dessa domäner båda finns i skog A har de ett dubbelriktat föräldra-barn-förtroende med varandra. Uppställningen visas på följande bild:

Forests setup

Domänerna forest-a och forest-b har båda en domänkontrollant och en medlemsserver (visas inte på bilden), medan de andra domänerna endast består av en enda domänkontrollant. Hela den här konfigurationen körs i Azure och hanteras via Terraform, som hanterar de virtuella maskinerna, nätverken, DNS och skogskonfigurationen, medan förtroendena har konfigurerats manuellt i efterhand.

Från A till B, vad finns i en PAC?

Antag att vi befinner oss i skog A och vill få åtkomst till resurser i skog B. Vi har vår Kerberos Ticket Granting Ticket (TGT), som är giltig i rotdomänen för den skog A som vi för närvarande befinner oss i (som lämpligen kallas forest-a). När vi vill komma åt resurser utanför vår nuvarande domän (antingen inom samma skog eller en annan skog) behöver Windows en tjänstebiljett för resursen, i det här exemplet forest-b-server.forest-b.local. Vår TGT för forest-a är naturligtvis värdelös i forest-b eftersom den är krypterad med hash för krbtgt-kontot i forest-a, vilket forest-b inte har. I Kerberos-termer autentiserar vi oss i ett annat område. Så vad vår klient gör i bakgrunden är att begära en tjänstebiljett för den resurs vi försöker komma åt i forest-b vid domänkontrollanten (DC) för forest-a. DC:n (som i Kerberos-termer är ett Key Distribution Center, eller KDC) har ingen resurs lokalt med suffixet forest-b.local, utan letar efter eventuella förtroenden med skogar som har det här suffixet.

Då det finns ett tvåvägsförtroende mellan skog A och skog B kan DC:n för forest-a utfärda oss en remitteringsbiljett (TGT) till forest-b. Den här biljetten är signerad med Kerberos nyckel för förtroendet mellan olika områden och innehåller de grupper som vi är medlemmar i i forest-a. Forest B kommer att acceptera den här biljetten och ge oss en tjänstebiljett för forest-b-server, som vi kan använda för att autentisera oss. En schematisk översikt visas nedan:

inter-realm kerberos

När vi ansluter på vår arbetsstation i skog A till servern i skog B kan vi se biljetterna med kommandot klist:

tickets involved

Den andra biljetten uppifrån är vår initiala TGT, som vi kan använda för att begära TGT för forest-b. Du kan se att denna TGT för forest-b (högst upp) har krbtgt principal för forest-b i serverfältet. Detta är det konto i skog A som är associerat med förtroendet (det här kontot heter forest-b$ och finns i Users-delen av katalogen). Dess krypterade del är krypterad med inter-realm trust key som dessa domäner delar.

Den tredje biljetten uppifrån är den biljett som vi kan använda i skog B för att kontakta servern där. Det är en tjänstebiljett som DC:n i skog B gett oss. Som du kan se i fältet Server gavs den här biljetten ut och är giltig i Kerberos-området FOREST-B.LOCAL.

Nu ska vi dyka ner i vad som faktiskt finns i den här biljetten. Varje Kerberos TGT som begärs av Windows innehåller ett Privilege Attribute Certificate, eller PAC. Formatet beskrivs i MS-PAC på MSDN. Denna PAC innehåller bland annat SID:erna för de grupper som vi är medlemmar i. För att kunna se vad som faktiskt finns i PAC måste vi först hämta biljetterna från minnet. För detta kommer vi att använda Mimikatz, med vilken vi kan dumpa alla Kerberos-biljetter till disk med kommandot sekurlsa::tickets /export. Även om Mimikatz faktiskt innehåller en del kod för felsökning av Kerberos, kunde jag inte räkna ut hur den fungerar, och jag kan inte skriva C så att modifiera något var i stort sett uteslutet ändå. Som tur är stöder mitt favoritbibliotek impacket i Python alla typer av Kerberos-grejer. Impacket tar emot Kerberos-biljetter i ccache-format, vilket inte är det format som Mimikatz exporterar, men vi kan enkelt konvertera dessa med kekeo. Vi behöver bara köra kommandot misc::convert ccache ourticket.kirbi och Kekeo sparar det som en .ccache-fil som vi kan läsa med Impacket.

För att dekryptera PAC har jag skrivit några verktyg baserade på impacket-exempel, som jag släpper som en del av denna forskning. För att dekryptera de krypterade delarna av Kerberos-biljetterna behöver vi naturligtvis krypteringsnycklarna, så jag har extraherat dessa för alla domäner med hjälp av secretsdump.py och dess dcsync-implementering. För den första TGT måste vi lägga till krbtgt hash till filen. Baserat på skärmbilden ovan kan du se att vi kommer att behöva aes256-cts-hmac-sha1-96-nyckeln som dumpats av secretsdump.

Verktyget getcfST.py används för att begära en servicebiljett i en annan skog baserat på en TGT i filen .ccache (du kan ange vilken fil som ska användas med export KRB5CCNAME=myticket.ccaches). Detta kommer att dekryptera och dumpa hela PAC, vars utdata kan ses här för de som är intresserade. Jag har lagt till några rader kod som skriver ut de viktiga delarna för oss i ett mer lättläst format:

Username: superuserDomain SID: S-1-5-21-3286968501-24975625-1618430583UserId: 500PrimaryGroupId 513Member of groups: -> 513 (attributes: 7) -> 520 (attributes: 7) -> 512 (attributes: 7) -> 519 (attributes: 7) -> 518 (attributes: 7)LogonServer: forest-a-dcLogonDomainName: forest-aExtra SIDS: -> S-1-18-1

Detta är en ganska vanlig PAC. Vi ser att vi är medlemmar i flera grupper, och eftersom det här är administratörskontot (ID 500, även om det har ett annat namn) för Forest A som vi för närvarande testar med, är det medlem i flera standardadministratörsgrupper, t.ex. Domain Admins (512) och Enterprise Admins (519). Vi har också Extra SID S-1-18-1, vilket indikerar att vi autentiserar baserat på bevis på innehav av autentiseringsuppgifter.

För att dekryptera den andra TGT:n måste vi ändra nyckeln från krbtgt-kontot till den för forest-b$-kontot, vilket är inter-realm trust key. I det här fallet krypteras PAC med RC4, som använder NT-hash som indata (ja, den som du använder för att skicka över hash). Detta är standard om inte rutan ”Den andra domänen stöder Kerberos AES-kryptering” är markerad. Som framgår av den råa dumpningen ändrades inte PAC, vilket stödjer antagandet att DC bara återkrypterar PAC som en del av biljetten med interrealm trust key för Forest B.

TGS:en är en något annorlunda historia. Förutom att det krävs några ändringar i exemplet getcfST.py och att vi måste ange AES-256-nyckeln för datorkontot forest-b-server för att dekryptera den, kan vi se att mer information har lagts till i PAC:n (rå dump här):

Username: superuserDomain SID: S-1-5-21-3286968501-24975625-1618430583UserId: 500PrimaryGroupId 513Member of groups: -> 513 (attributes: 7) -> 520 (attributes: 7) -> 512 (attributes: 7) -> 519 (attributes: 7) -> 518 (attributes: 7)LogonServer: forest-a-dcLogonDomainName: forest-aExtra SIDS: -> S-1-18-1Extra domain groups found! Domain SID:S-1-5-21-2897307217-3322366030-3810619207Relative groups: -> 1107 (attributes: 536870919)

Vi ser att en ny sektion har lagts till, som innehåller domän-SID:t för domänen forest-b och en grupp som vårt konto är medlem i i Forest B. Dessa SID är en del av ResourceGroup-strukturerna i PAC och används för att lagra medlemskap i eventuella lokala domängrupper i forest-b-domänen. Som förklaras i det här inlägget av harmj0y är domänlokala grupper de enda grupper som kan innehålla säkerhetsprinciper från andra skogar. I forest-b-domänen är vår superuser-användare från skog A medlem i gruppen Testgroup2, vilket du kan se nedan.

Biljetter inblandade

Eftersom detta återspeglas i vår PAC, som de servrar som vi autentiserar oss till med hjälp av vår Kerberos-tjänstebiljett använder för auktorisering, kommer alla privilegier som tilldelas Testgroup2 att gälla superanvändarkontot från den andra skogen. Detta är hur autentisering och auktorisering fungerar över trusts.

Golden tickets och SID-filtrering

För ett par år sedan arbetade Sean Metcalf och Benjamin Delphy tillsammans för att lägga till stöd för SID-historik i Mimikatz, vilket gjorde det möjligt att eskalera från en Active Directory-domän till en annan inom samma skog. Proceduren för detta beskrivs i detalj här. Hur översätts detta till förtroenden med en annan skog? Låt oss skapa en gyllene biljett med några intressanta SID:er för att se hur de behandlas när de passerar skogsgränsen. Vi använder följande Mimikatz-kommando för att skapa en gyllene biljett i vår nuvarande skog:

kerberos::golden /domain:forest-a.local /sid:S-1-5-21-3286968501-24975625-1618430583 /rc4:2acc1a3824a47c4fcb21ef7440042e85 /user:Superuser /target:forest-a.local /service:krbtgt /sids:S-1-5-21-3286968501-24975625-1618430583-1604,S-1-5-21-3286968501-24975625-1111111111-1605,S-1-18-1,S-1-5-21-2897307217-3322366030-3810619207-1106 /ptt

Låt oss dela upp det här kommandot. Vi skapar en gyllene biljett i forest-a, signerad med krbtgt hash för forest-a. Som extra SID:er inkluderar vi några intressanta SID:er:

  • S-1-5-21-3286968501-24975625-1618430583-1604, SID för en grupp som vi egentligen inte är medlemmar i
  • S-1-5-21-3286968501-24975625-1111111111-1605, SID för en domän som egentligen inte existerar
  • S-1-18-1, SID som Windows lägger till för att indikera att vi autentiserat oss med bevis på innehav av credentals
  • S-1-5-21-2897307217-3322366030-3810619207-1106, en grupp i forest-b

skapar en gyllene biljett med Mimikatz

Flaggan /ptt injicerar biljetten i minnet, och när vi surfar till \forest-b-server.forest-b.local ser vi inget felmeddelande, vilket indikerar att biljetten användes framgångsrikt för att komma åt en resurs i forest-b. Vi exporterar biljetterna som tidigare och dekrypterar dem på samma sätt som i föregående avsnitt.

TGT:n för forest-a innehåller de förväntade SID:arna:

Username: SuperuserDomain SID: S-1-5-21-3286968501-24975625-1618430583UserId: 500PrimaryGroupId 513Member of groups: -> 513 (attributes: 7) -> 512 (attributes: 7) -> 520 (attributes: 7) -> 518 (attributes: 7) -> 519 (attributes: 7)LogonServer: LogonDomainName: FOREST-AExtra SIDS: -> S-1-5-21-3286968501-24975625-1618430583-1604 -> S-1-5-21-3286968501-24975625-1111111111-1605 -> S-1-18-1 -> S-1-5-21-2897307217-3322366030-3810619207-1106

TGT:n som vi fick för forest-b från domänkontrollanten för forest-a, signerad med inter-realm trust key, innehåller faktiskt exakt samma information:

Username: SuperuserDomain SID: S-1-5-21-3286968501-24975625-1618430583UserId: 500PrimaryGroupId 513Member of groups: -> 513 (attributes: 7) -> 512 (attributes: 7) -> 520 (attributes: 7) -> 518 (attributes: 7) -> 519 (attributes: 7)LogonServer: LogonDomainName: FOREST-AExtra SIDS: -> S-1-5-21-3286968501-24975625-1618430583-1604 -> S-1-5-21-3286968501-24975625-1111111111-1605 -> S-1-18-1 -> S-1-5-21-2897307217-3322366030-3810619207-1106

Detta tyder återigen på att DC inte validerar PAC:n, utan bara signerar den på nytt med inter-realm-nyckeln för forest-b, trots att den innehåller en grupp som vi faktiskt inte är medlemmar i.

När vi presenterar denna TGT för DC i forest-b får vi tillbaka vårt servicebiljett, som har följande PAC:

Username: SuperuserDomain SID: S-1-5-21-3286968501-24975625-1618430583UserId: 500PrimaryGroupId 513Member of groups: -> 513 (attributes: 7) -> 512 (attributes: 7) -> 520 (attributes: 7) -> 518 (attributes: 7) -> 519 (attributes: 7)LogonServer: LogonDomainName: FOREST-AExtra SIDS: -> S-1-5-21-3286968501-24975625-1618430583-1604 -> S-1-18-1Extra domain groups found! Domain SID:S-1-5-21-2897307217-3322366030-3810619207Relative groups: -> 1107 (attributes: 536870919)

Vad hände här? Vi ser att våra medlemskap i forest-b-domänen återigen har lagts till i PAC:n, men att vissa SID:er har filtrerats bort. Det är här som säkerhetsmekanismen för SID-filtrering har trätt in och filtrerat bort alla SID:er som inte är en del av forest-a. Reglerna för SID-filtrering beskrivs i MSDN. Intressanta regler här är de med ForestSpecific-post. Dessa SID:er är endast tillåtna från en PAC inom skogen. Eftersom vår PAC kommer utanför skogen kommer dessa SID:er alltid att filtreras från vår PAC. De tre reglerna efter ForestSpecific-reglerna ser till att alla SID:er som inte kommer från skog A filtreras bort. Detta inkluderar både den icke-existerande SID som vi levererade, samt alla icke ForestSpecific SID som finns i skog B.

Det gör det fortfarande möjligt för oss att låtsas vara vilken användare som helst i skog A, så om användare från skog A har fått särskilda privilegier i skog B (vilket förmodligen är anledningen till att hela förtroendet sattes upp till att börja med), så äventyras dessa nu.

SID-filtrering relaxation

Vad som fångade min uppmärksamhet tidigt i den här forskningen är ett alternativ för förtroenden som endast är tillgängligt via netdom-verktyget, och som inte syns i det grafiska gränssnittet. På en av sidorna i Microsofts dokumentation beskrivs att man tillåter SID-historik på skogsöverskridande förtroenden. Vad gör detta? Låt oss aktivera SID-historik på förtroendet från skog B till A (vilket påverkar användare som autentiserar sig från A i B):

C:\Users\superuser>netdom trust /d:forest-a.local forest-b.local /enablesidhistory:yesEnabling SID history for this trust.The command completed successfully.

Så vad ändrades? Låt oss se hur detta översätts till TrustAttributes-flaggan för Trusted Domain Object. Du kan fråga efter detta med hjälp av flera verktyg, nedan ser du resultatet av domain_trusts.html-filen från ldapdomaindump som körs mot skog B, vilket är ett verktyg som jag skrev för ett tag sedan för att samla in AD-information.

trust flags for forest-b

Vårt förtroende med skog A har nu flaggan TREAT_AS_EXTERNAL. I den relevanta Microsoft-dokumentationen står följande:

Om den här biten är inställd ska ett skogsöverskridande förtroende till en domän behandlas som ett externt förtroende i samband med SID-filtrering. Förtroenden över skogsgränserna filtreras strängare än externa förtroenden. Det här attributet gör att dessa korsvisa skogsförtroenden blir likvärdiga med externa förtroenden. Mer information om hur varje förtroendetyp filtreras finns i avsnitt 4.1.2.2.2.

Detta pekar tillbaka till avsnittet i som beskriver SID-filtrering. Låt oss se vad som händer om vi erbjuder samma TGT mot DC forest-b:

Username: SuperuserDomain SID: S-1-5-21-3286968501-24975625-1618430583UserId: 500PrimaryGroupId 513Member of groups: -> 513 (attributes: 7) -> 512 (attributes: 7) -> 520 (attributes: 7) -> 518 (attributes: 7) -> 519 (attributes: 7)LogonServer: LogonDomainName: FOREST-AExtra SIDS: -> S-1-5-21-3286968501-24975625-1618430583-1604 -> S-1-5-21-3286968501-24975625-1111111111-1605 -> S-1-18-1 -> S-1-5-21-2897307217-3322366030-3810619207-1106Extra domain groups found! Domain SID:S-1-5-21-2897307217-3322366030-3810619207Relative groups: -> 1107 (attributes: 536870919)

Vårt serviceärende från DC forest-b innehåller nu alla SID:er som vi lade in i vårt tidigare Mimikatzärende! Detta innebär att vi kan ange vilken SID som helst som inte är filtrerad som ForestSpecific i vår PAC och att den kommer att accepteras av DC för skog B.

Låt oss skapa en ny gyllene biljett med några fler SID för att testa denna hypotes:

kerberos::golden /domain:forest-a.local /sid:S-1-5-21-3286968501-24975625-1618430583 /rc4:b8e9b4b3feb56c7ba1575bf7fa3dc76f /user:Superuser /target:forest-b.local /service:krbtgt /sids:S-1-5-21-3286968501-24975625-1618430583-1604,S-1-5-21-3286968501-24975625-1111111111-1605,S-1-18-1,S-1-5-21-2897307217-3322366030-3810619207-1106,S-1-5-21-2897307217-3322366030-3810619207-512,S-1-5-21-2897307217-3322366030-3810619207-519,S-1-5-21-2897307217-3322366030-3810619207-548,S-1-5-21-2897307217-3322366030-3810619207-3101

De nya SID som ingår här:

  • S-1-5-21-2897307217-3322366030-3810619207-512: Domain Admins, bör filtreras av en uttrycklig ForestSpecific regel
  • S-1-5-21-2897307217-3322366030-3810619207-519: Enterprise Admins, bör filtreras av en uttrycklig ForestSpecific regel
  • S-1-5-21-2897307217-3322366030-3810619207-548: Account Operators, bör filtreras av ForestSpecific-regeln som inte tillåter SID:er mellan 500 och 1000.
  • S-1-5-21-2897307217-3322366030-3810619207-3101: Är en grupp som är medlem i Domain Admins, bör inte filtreras.

Som du kanske har lagt märke till är ovanstående faktiskt signerat med den interrealmiska förtroendeskyddsnyckeln, så vi skapar direkt TGT:en som är giltig för Forest B här, för att hoppa över steget med att erbjuda den till Forest A DC först.

Nu får vi följande tillbaka i PAC:

Username: SuperuserDomain SID: S-1-5-21-3286968501-24975625-1618430583UserId: 500PrimaryGroupId 513Member of groups: -> 513 (attributes: 7) -> 512 (attributes: 7) -> 520 (attributes: 7) -> 518 (attributes: 7) -> 519 (attributes: 7)LogonServer: LogonDomainName: FOREST-AExtra SIDS: -> S-1-5-21-3286968501-24975625-1618430583-1604 -> S-1-5-21-3286968501-24975625-1111111111-1605 -> S-1-18-1 -> S-1-5-21-2897307217-3322366030-3810619207-1106 -> S-1-5-21-2897307217-3322366030-3810619207-3101Extra domain groups found! Domain SID:S-1-5-21-2897307217-3322366030-3810619207Relative groups: -> 1107 (attributes: 536870919)

Vi har några saker att notera:

  • Grupperna DA/EA/Account Operators tas faktiskt bort av SID-filtreringen
  • Gruppen Domain Admins läggs inte till i ResourceGroup-delen av PAC, trots att gruppen 3101 är en direkt medlem av denna grupp. Detta beror på att gruppen Domain Admins är en global grupp, medan endast lokala domängrupper läggs till i PAC.
  • Vad detta innebär för en angripare är att du kan förfalska vilken RID >1000-grupp som helst om SID-historik är aktiverad över ett skogsförtroende! I de flesta miljöer kan en angripare på så sätt äventyra skogen. Till exempel har Exchange-säkerhetsgrupperna, som i många konfigurationer möjliggör en upptrappning av privilegier till DA, alla RID:er som är större än 1000. Många organisationer har också anpassade grupper för arbetsstationsadministratörer eller helpdesks som får lokala administratörsrättigheter på arbetsstationer eller servrar. Jag har till exempel just gett gruppen IT-Admins (med RID 3101 som är en del av vår gyllene biljett) administratörsrättigheter på maskinen forest-b-server. Efter att ha bytt ut vår TGT mot en tjänstebiljett kan vi autentisera oss med den här biljetten på boxen:

    Administratörsåtkomst via förfalskat medlemskap

    Slutsatser

    Trusts över skogsgränserna är som standard strängt filtrerade och tillåter inte att SID:er utanför den skogen färdas över trustet. En angripare som äventyrar en skog som du litar på kan dock utge sig för att vara en användare i den skogen och på så sätt få tillgång till resurser som uttryckligen har beviljats användare/grupper i den skogen.

    Om SID-historik är aktiverad för ett förtroende över skogsgränserna försvagas säkerheten avsevärt och angripare kan utge sig för att vara gruppmedlem i vilken grupp som helst med ett RID som är större än 1000, vilket i de flesta fall kan leda till att skogen äventyras.Om du är IT-administratör bör du noga överväga vilka användare i olika skogar som du beviljar åtkomst till din skog, eftersom varje användare som beviljas åtkomst försvagar säkerhetsgränsen mellan skogarna. Jag skulle inte rekommendera att tillåta SID-historik mellan skogar om det inte är absolut nödvändigt.

    I nästa del (eller delar, vem vet) kommer vi att dyka ner i hur trust Transitivity fungerar och diskutera andra typer av trusts med domäner utanför skogen. Detta innebär att vi även kommer att börja leka med Forest C och sub.forest-a.

    Verktygen

    Verktygen som används i det här inlägget är tillgängliga som proof-of-concept på min GitHub. Dessa verktyg kommer att kräva manuell modifiering för att vara användbara och tillhandahålls endast AS-IS till personer som vill reproducera eller dyka vidare i denna forskning.

Lämna ett svar

Din e-postadress kommer inte publiceras.