Перейти до тексту
До вебінару Шлях до Tier 0

Методичка ST&T · Forestall

Шлях до Tier 0

Робочий зошит самоперевірки: сім списків про власну інфраструктуру ідентичностей

Версія 1.0 · серпень 2026 · Active Directory + Microsoft Entra ID

ST&T — дистрибʼютор Forestall (identity security posture management)

Зміст

Розділ 0. Перед стартом

Частина I. Хто вже всередині

  1. Реальний склад Domain Admins та інших привілейованих груп
  2. Небезпечні ACL на привілейованих групах: хто може дописати себе в Tier 0

Частина II. Шляхи повз членство

  1. Делеговані права поза стандартними контейнерами: ACL на OU, корінь домену, DCSync
  2. Делегування Kerberos: unconstrained, constrained, protocol transition, RBCD
  3. Групові політики: хто створює, хто лінкує, хто редагує — і що лежить у SYSVOL

Частина III. Хмара і стик

  1. Entra ID: ролі, постійні призначення, застосунки і гості
  2. Гібридний стик: Entra Connect, синхронізовані привілеї, Seamless SSO

Що ще варто перевірити

Розділ 13. Що ви щойно зробили — і чого не змогли Розділ 14. Безкоштовні інструменти: що вони закривають, а що ні Розділ 15. Що далі

Розділ 0. Перед стартом

Тільки читання

Жодної команди, що змінює стан системи. Дозволений набір — Get-, Search-, Select-, Where-, Export-.

Мінімальні права

Звичайний доменний користувач у локальному AD і роль Global Reader або Security Reader у тенанті. Domain Admin не потрібен.

Нічого не надсилається

Результати ваших перевірок нам не потрібні. Аркуш екстраполяції рахує локально, прогрес читання лишається у вашому браузері.

Одне слово, без якого далі не буде сенсу: Tier 0

Tier 0 — це не група в AD і не прапорець на обʼєкті. Це множина обʼєктів, компрометація будь-якого з яких дає контроль над усім доменом або лісом. Модель рівнів запропонувала Microsoft: Tier 0 — каталог і все, що ним керує; Tier 1 — сервери й застосунки; Tier 2 — робочі станції й користувачі.

До Tier 0 належать не лише Domain Admins. Це контролери домену і їхні компʼютерні обʼєкти, krbtgt, обʼєкт кореня домену, AdminSDHolder, GPO, злінковані на OU Domain Controllers, сервер Entra Connect, сервери AD FS — і будь-який обліковий запис чи група, що має право записувати в будь-що з переліченого.

Головне практичне правило моделі: обʼєкт нижчого рівня не має права керувати обʼєктом вищого. Помилки, які шукає ця методичка, — це майже завжди порушення саме цього правила: хтось із Tier 2 має право, що дотягується до Tier 0.

Тому питання «хто фактично в Tier 0» тут означає: хто фактично може отримати контроль над доменом — за членством, за правом на обʼєкт, за делегуванням або через хмару.

І чотири слова, якими цей документ закінчиться

Уявіть каталог не таблицею, а схемою:

  • вузол — обʼєкт: користувач, група, компʼютер, OU, GPO;
  • крок — одне право одного вузла на інший: «Helpdesk-L2 може скинути пароль svc-backup»;
  • ланцюг — кілька кроків поспіль, де кінець одного стає початком наступного;
  • choke point, точка розриву — один крок, через який проходить багато ланцюгів; виправлення такого кроку закриває їх усі одразу.

Сім перевірок дають вам вузли. Кроки ви побачите частково, ланцюги — ніде. Чому так і що з цим робити — розділ 13.

Що це за документ

Це робочий зошит, а не whitepaper. Він побудований так, щоб інженер, який має доступ до домену на рівні звичайного користувача і читацьку роль у тенанті, за приблизно 90 хвилин зібрав сім конкретних списків про власну інфраструктуру ідентичностей — і побачив те, чого зазвичай не видно з консолі щоденного адміністрування.

Що ви отримаєте за 90 хвилин:

  • сім списків (розділи 1–7), кожен — відповідь на питання «хто» або «що»: хто фактично в Tier 0, де висять делеговані права поза стандартними контейнерами, які акаунти мають SPN, де ввімкнено делегування Kerberos, що лежить в AdminSDHolder, який стик між локальним AD і Entra;
  • для кожної перевірки — два шляхи виконання: клік у нативній консолі та команда для копіювання;
  • для кожної перевірки — розуміння, який слід вона лишає в логах і що з цього побачить SOC;
  • для кожної перевірки — чесно окреслену межу: що саме цим способом побачити неможливо.

Чого ви за 90 хвилин не отримаєте:

  • оцінки ризику. Списки не ранжовані. Документ не скаже, що виправляти першим;
  • ланцюгів. Ви отримаєте вузли, а не маршрути (див. розділ 13);
  • висновку «ви зламані» або «ви в безпеці». Жодна з перевірок не є детектом компрометації;
  • готового звіту для керівництва. Матеріал інженерний і в такому вигляді нагору не йде.

Інваріант, який ви перевіряєте самі, а не вірите нам на слово

У цьому документі немає жодної команди, яка змінює стан системи.

Не «ми старалися», не «майже немає» — немає. Перевіряється це за тридцять секунд пошуком по файлу. Ось повний перелік дієслів, які ми в цьому документі заборонили собі самі:

text
Set-  New-  Remove-  Add-  Enable-  Disable-  Grant-  Revoke-
Reset-  Move-  Rename-  Restore-  Clear-  Update-  Install-

Відкрийте файл, натисніть пошук, введіть будь-який із цих префіксів. Єдине місце у всьому документі, де вони зустрічаються, — цей абзац. Якщо пошук дає збіг деінде — це наша помилка, і ми хочемо про неї знати.

Дозволений набір, який ви побачите у семи перевірках:

  • читання каталогу — Get-*, Search-*, Connect-MgGraph (встановлення читацької сесії до Graph: це автентифікація, а не операція над каталогом);
  • обробка вже прочитаного, на вашій машині — Select-Object, Where-Object, ForEach-Object, Group-Object, Sort-Object, Select-String, конструктори [pscustomobject] і RawSecurityDescriptor;
  • робота з файлами — Get-ChildItem (перелік файлів у SYSVOL), Export-Csv (запис у файл на вашій машині, не в каталог);
  • консольні утиліти — dsacls без ключів запису, nltest, repadmin /showattr.

Командлети групи *-Object і Select-String до каталогу не звертаються взагалі: вони працюють із даними, які вже лежать у памʼяті вашого PowerShell. Заборонених дієслів немає в жодному з них — і саме це ви перевіряєте пошуком за тридцять секунд.

Це не формальність. Читати каталог і змінювати каталог — різні за наслідками дії. Документ, який дає інженерові прочитати щось про власну інфраструктуру, не має права навіть теоретично щось у ній зіпсувати. Тому інваріант жорсткий і перевіряється механічно.

Мінімальні права для всього набору

Локальний AD: звичайний доменний користувач. Не delegated admin, не Account Operator, не Domain Admin. Група Authenticated Users за замовчуванням має право читати більшість атрибутів обʼєктів і їхні дескриптори безпеки — цього вистачає для всіх семи перевірок.

Чесні винятки, де звичайного користувача не вистачить і ми про це попереджаємо просто в тілі перевірки:

  • атрибути з прапорцем CONFIDENTIAL (searchFlags з бітом 0x80) читає лише той, кому явно делеговано CONTROL_ACCESS;
  • паролі LAPS (ms-Mcs-AdmPwd, msLAPS-Password, msLAPS-EncryptedPassword) і blob gMSA (msDS-ManagedPassword) — ті самі правила, і ми їх не читаємо взагалі;
  • контейнер Deleted Objects — читання потребує окремо делегованого права;
  • членство в локальних групах адміністраторів на серверах і робочих станціях доменним користувачем з централізованої точки не читається. Це не деталь — це одна з чотирьох меж, до яких ми повернемося в розділі 13.

Entra ID: роль Global Reader або Security Reader. Обидві — read-only за визначенням. Для журналів входів достатньо Reports Reader.

Чесний виняток і тут: якщо ви йдете шляхом команд через Microsoft.Graph PowerShell, то read-only scopes (Directory.Read.All, RoleManagement.Read.Directory, Policy.Read.All, Application.Read.All, AuditLog.Read.All) потребують адміністративної згоди в тенанті — один раз, від адміністратора. Ця згода не робить вас адміністратором: вона лише дозволяє застосунку читати те, що ваша роль і так дозволяє бачити в порталі. Якщо погодження на це немає — використовуйте ручний шлях через портал, він від згоди не залежить.

Скажіть SOC заздалегідь

Це не ввічливість, це операційна необхідність.

Масове читання дескрипторів безпеки, перелік акаунтів з SPN, обхід OU і запити до Configuration NC — телеметрично не відрізняються від першої фази розвідки в реальній атаці. Так само виглядає SharpHound — найпоширеніший збирач даних для BloodHound (див. розділ 14). Так само виглядає скрипт зловмисника, який щойно отримав доменний акаунт.

Один термін, який далі буде постійно. У кожного обʼєкта AD є дескриптор безпеки, і в ньому дві незалежні частини: DACL — хто що може робити з обʼєктом (вкладка Security) і SACL — що саме з дій над обʼєктом записувати в журнал аудиту (вкладка Auditing). За замовчуванням SACL порожній майже скрізь.

Практичний наслідок, який і треба сказати SOC: читання обʼєкта, на якому немає SACL, не породжує жодної події — не тому, що щось приховали, а тому, що писати подію нікому не наказували. Перевірити це самому ви не зможете: читання SACL потребує привілею SeSecurityPrivilege, якого в рядового користувача немає.

Що конкретно може спрацювати:

  • Microsoft Defender for Identity, якщо сенсори стоять на контролерах домену: сигнали типу Security principal reconnaissance (LDAP) (перші 15 днів після розгортання сенсора — період навчання, сигнал може не спрацювати) і User and Group membership reconnaissance (SAMR);
  • Event ID 1644 у журналі Directory Service на DC — «дорогий» або неефективний LDAP-пошук. Пишеться, тільки якщо ввімкнено діагностичний рівень 15 Field Engineering; у більшості середовищ він вимкнений, тож сліду не буде;
  • Event ID 4662 (операція над обʼєктом каталогу) — пишеться, тільки якщо на обʼєктах налаштовано SACL. Без SACL ваші читання просто не логуються, і це варто знати обом сторонам;
  • Event ID 5145 — доступ до мережевої шари, релевантно при читанні SYSVOL/GPO, за умови ввімкненого Detailed File Share auditing;
  • Event ID 4624/4634 — звичайні входи, самі по собі нічого не значать, але корелюються за часом;
  • на боці Entra: журнал входів (інтерактивних і неінтерактивних) і Microsoft Graph activity logs. Останні пишуться лише за наявності ліцензії Entra ID P1/P2 і ввімкненого diagnostic setting; журнал аудиту Entra фіксує зміни, а не читання, тож ваших Get- там не буде.

Формулювання для листа в SOC, яке працює: «Дата, час-вікно, від якого акаунта, з якої машини, характер активності — читання каталогу для внутрішнього аудиту, змін не буде, ось перелік розділів». Двох речень достатньо. Різниця між «попередив» і «не попередив» — це різниця між рядком у журналі змін і інцидентом о третій ночі.

Не надсилайте нам результати

Прямо і без підтексту: результати ваших перевірок нам не потрібні. Ні у вигляді CSV, ні скріншотами, ні «на подивитися». Ми їх не просимо, не приймаємо і не маємо де зберігати.

Це дані, що описують поверхню атаки вашої організації. Їхнє місце — у вашому контурі. Якщо після методички захочеться поговорити — приходьте з питаннями, порядками величин («у нас приблизно триста обʼєктів з нестандартними ACL») і симптомами. Цього більш ніж достатньо для змістовної розмови, і це нікого не наражає.

Що потрібно встановити (і як обійтися без цього)

Для командного шляху:

  • RSAT з компонентом Active Directory Module for Windows PowerShell — для командлетів Get-AD*;
  • модуль Microsoft.Graph PowerShell — для читацьких запитів до Entra;
  • dsacls, nltest, repadmin входять до складу RSAT / ролі AD DS.

Для ручного шляху не потрібно нічого, чого немає на типовій адмінській машині: ADUC (dsa.msc), ADSI Edit (adsiedit.msc), GPMC (gpmc.msc), Active Directory Domains and Trusts (domain.msc), dssite.msc, портал Entra у браузері. Кожна перевірка описана так, щоб її можна було виконати мишею.

Це свідома конструкція. Далеко не в кожній організації інженер має право поставити модуль на робочу машину, і далеко не кожен хоче. Ручний шлях у цьому документі — головний, команда — прискорення для тих, кому зручніше клавіатурою.

Спільна преамбула до командного шляху

Пʼять із семи перевірок працюють із локальним AD і спираються на два спільні рішення, які варто ухвалити один раз на початку.

Рішення перше: до якого контролера домену ви звертаєтеся. Транзитивні запити з правилом ланцюга (1.2.840.113556.1.4.1941) і масове читання дескрипторів безпеки виконуються на DC, а не на вашому клієнті. Без явного -Server локатор віддасть довільний контролер — у невеликих доменах це часто PDC Emulator, той самий, на якому крутиться SDProp, обробка блокувань і зміни паролів. Оберіть інший.

Рішення друге: до чого привʼязані фільтри. Імена вбудованих груп локалізуються на локалізованому Windows Server і перейменовуються за десять років життя домену. Фільтр, привʼязаний до CN=Domain Admins, у такому домені поверне не помилку, а порожній результат — тобто тихо відзвітує «все чисто» саме там, де мав знайти проблему. Це найнебезпечніший клас дефекту в аудиті. Тому спершу резолвимо DN за well-known SID і далі підставляємо у фільтри вже фактичні DN.

PowerShell
# ПРЕАМБУЛА. Виконати один раз; змінні використовуються в перевірках 1–5.

$dom  = Get-ADDomain
$dSid = $dom.DomainSID.Value

# Кого САМЕ не треба навантажувати важкими запитами:
$dom.PDCEmulator

# Підставте узгоджений з командою інфраструктури контролер (НЕ PDC Emulator):
$dc = "DC02.corp.local"

# Well-known SID замість імен. Доменні: 512 Domain Admins, 516 Domain Controllers,
# 518 Schema Admins, 519 Enterprise Admins, 520 Group Policy Creator Owners,
# 521 Read-only Domain Controllers, 526 Key Admins, 527 Enterprise Key Admins.
# Builtin: 544 Administrators, 548 Account Operators, 549 Server Operators,
# 550 Print Operators, 551 Backup Operators, 552 Replicator.
$sids = @(
  "$dSid-512","$dSid-516","$dSid-518","$dSid-519","$dSid-520","$dSid-521","$dSid-526","$dSid-527",
  "S-1-5-32-544","S-1-5-32-548","S-1-5-32-549","S-1-5-32-550","S-1-5-32-551","S-1-5-32-552"
)

$protected = $sids | ForEach-Object { Get-ADGroup -Server $dc -Identity $_ -ErrorAction SilentlyContinue }
$protectedDNs = $protected.DistinguishedName
$protected | Select-Object @{n="SID";e={$_.SID.Value}}, Name, DistinguishedName

Правило нульового результату. Якщо будь-який запит у розділах 1–12 повернув 0 рядків, це спершу трактується як помилка DN або імені, і лише після того, як ви побачили фактичний DN у виводі преамбули, — як реальна порожня група.

Правило таймауту. Транзитивний обхід ланцюга не індексується. На домені з десятками тисяч обʼєктів запит може впертися в MaxQueryDuration (типове значення — 120 секунд) і бути скасованим контролером з adminLimitExceeded. Якщо це сталося — не перезапускайте команду повторно: кожен перезапуск коштує DC ще стільки ж процесорного часу. Звузьте -SearchBase до конкретної OU або перейдіть на обхід member/memberOf рівень за рівнем на клієнті. Перший запуск важких запитів робіть поза робочими годинами і з заміром часу.

Прозорість наміру

ST&T — дистрибʼютор Forestall, платформи класу ISPM (identity security posture management). Ця методичка — лід-магніт. Ми зробили її корисною самою по собі, але робили не з альтруїзму: мета — щоб після семи списків у вас виникли питання, на які цей документ чесно не відповідає, і щоб ви прийшли з ними на вебінар 27.08 або в приватну розмову.

Кажемо це на початку, а не дрібним шрифтом у кінці, з практичної причини: ви маєте читати технічні твердження документа, знаючи, хто і навіщо їх написав. Усе, що можна перевірити, — перевіряйте. Ми навмисне не вставляли рекламу в поле «чого цим НЕ побачите»: там тільки технічні межі методу, без згадок продукту.

Дисклеймер, який варто прочитати до, а не після

Це не список вразливостей. Жоден із семи списків не є переліком дірок.

Делеговане право Reset Password у helpdesk — не дефект, це їхня робота. Сервісний акаунт з SPN — не дефект, так працює Kerberos. Постійна роль у Entra — не дефект, комусь треба тримати світло ввімкненим. Необмежене делегування на старому сервері — можливо, дефект, а можливо, єдиний спосіб, у який працює бухгалтерська система, яку ніхто не має права зупинити.

Правильне питання до кожного рядка ваших списків — не «це погано?», а «я можу пояснити, чому це саме так, і хто це вирішив?». Рядок, який ви пояснити не можете, — не вразливість. Це прогалина у вашій моделі власної інфраструктури. Саме такі прогалини ми і хочемо вивести на поверхню.

Частина I. Хто вже всередині

Реальний склад Domain Admins та інших привілейованих груп — з вкладеністю, первинними групами та foreign security principals

Що це і чому небезпечно

Вкладка Members у ADUC показує лише ПРЯМИХ членів, а реальний склад Tier 0 — транзитивне замикання: група в групі, в групі. У домені віком 10+ років типово: у Domain Admins три акаунти, доступ мають сорок — через «IT-Support-L3» → «Server-Admins» → вкладену універсальну групу з іншого домену. Другий сліпий сектор — foreign security principals: члени з чужого домену чи лісу лежать у CN=ForeignSecurityPrincipals обʼєктами з іменем-SID, і ADUC показує сирий SID або взагалі нерозвʼязане імʼя. Третій — компʼютери й сервісні акаунти всередині Tier 0 з паролем у конфігу застосунку. Четвертий, найтихіший, — членство за первинною групою: при primaryGroupID = 512 обліковий запис є повноцінним доменним адміністратором, але його немає ані в member групи, ані в memberOf (Microsoft документує, що задана первинна група виключає користувача зі списку членства). Тобто цієї техніки тихого закріплення не видно ні в Members, ні в транзитивному фільтрі — потрібен окремий запит.

Поки не пораховано транзитивний склад і первинні групи, «у нас 3 доменні адміни» — заява про перший рівень, а не про домен.

Як подивитися вручну

  1. Відкрити ADUC (dsa.msc) — з RSAT на робочій станції або сервері керування.
  2. Меню View → Advanced Features: без цього не видно ані ForeignSecurityPrincipals, ані Attribute Editor.
  3. Домен → Users → Domain Admins → Properties → Members: виписати склад, це ПРЯМІ члени, рівень 1.
  4. Кожного члена-групу розкрити так само й далі рекурсивно, доки не залишаться самі користувачі та компʼютери. Фіксувати глибину — вона й пояснює, чому цього ніхто не памʼятав.
  5. Повторити кроки 3–4 для: Builtin → Administrators; Users → Enterprise Admins, Schema Admins, Key Admins, Enterprise Key Admins.
  6. З боку користувача: обліковий запис → Attribute Editor → Filter → ConstructedtokenGroups. Це SID усіх груп, які DC поклав би в його токен, включно з вкладеними; Member Of дає лише прямі. Важливо: порожній tokenGroups не означає відсутності вкладених членств: Microsoft документує, що без доступного глобального каталогу атрибут не повертається — ADUC має бути підключений до DC з роллю GC (або запит іде на порт 3268). Читати точково, по одному обʼєкту: це обчислення на льоту, і Microsoft попереджає, що масові запити просаджують продуктивність контролера аж до втрати обробки інших запитів.
  7. Первинна група. Обліковий запис → Attribute Editor → primaryGroupID. Норма: 513 (Domain Users), 515 (Domain Computers) для станцій і рядових серверів, 516 для контролерів домену, 521 для RODC. Інше значення на користувачі — окремий рядок списку; з боку групи, на вкладці Members, цього не видно.
  8. Foreign security principals: контейнер ForeignSecurityPrincipals, обʼєкти названі за SID. Кожен → Attribute Editor → memberOf: у які локальні групи домену він входить. Administrators або Domain Admins серед них — член Tier 0, якого немає у вашому домені.
  9. Контекст трастів: Active Directory Domains and Trusts (domain.msc) → домен → Properties → Trusts: напрямок (Incoming / Outgoing / Two-way) і тип (External / Forest / Realm / Parent-Child) кожного трасту — від цього залежить, чи розвʼяжете ви FSP у імена. У ADUC цієї вкладки немає, це окрема оснастка.

Команда

PowerShell
# Спершу — ПРЕАМБУЛА з розділу 0 ($dom, $dSid, $dc, $protected, $protectedDNs).

$daDN  = ($protected | Where-Object { $_.SID.Value -eq "$dSid-512"   }).DistinguishedName
$admDN = ($protected | Where-Object { $_.SID.Value -eq "S-1-5-32-544" }).DistinguishedName

# 1. Прямі члени (member) — «як бачить ADUC»
Get-ADGroup -Server $dc -Identity $daDN -Properties member | Select-Object -ExpandProperty member

# 2. Транзитивний склад через LDAP-правило ланцюга 1.2.840.113556.1.4.1941.
#    Звузьте -SearchBase до конкретної OU, якщо знаєте, де живуть облікові записи.
Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName `
  -LDAPFilter "(memberOf:1.2.840.113556.1.4.1941:=$daDN)" `
  -Properties sAMAccountName,objectClass,servicePrincipalName |
  Select-Object sAMAccountName,objectClass,servicePrincipalName,DistinguishedName

# 3. Те саме для Builtin\Administrators — окрема група, окремий склад.
#    Запускати ОКРЕМО від попереднього запиту, а не одним OR-фільтром.
Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName `
  -LDAPFilter "(memberOf:1.2.840.113556.1.4.1941:=$admDN)" `
  -Properties sAMAccountName,objectClass |
  Select-Object sAMAccountName,objectClass,DistinguishedName

# 4. ЧЛЕНСТВО ЗА ПЕРВИННОЮ ГРУПОЮ. Його не видно ані в member, ані в memberOf,
#    ані у фільтрі 1941 — це окремий канал і окремий запит.
Get-ADUser -Server $dc -LDAPFilter "(|(primaryGroupID=512)(primaryGroupID=516)(primaryGroupID=518)(primaryGroupID=519)(primaryGroupID=520)(primaryGroupID=521)(primaryGroupID=526)(primaryGroupID=527))" `
  -Properties primaryGroupID,servicePrincipalName,whenChanged |
  Select-Object sAMAccountName,primaryGroupID,servicePrincipalName,whenChanged,DistinguishedName

Get-ADComputer -Server $dc -LDAPFilter "(|(primaryGroupID=512)(primaryGroupID=518)(primaryGroupID=519))" `
  -Properties primaryGroupID | Select-Object Name,primaryGroupID,DistinguishedName

# 5. Foreign security principals і те, куди вони входять
Get-ADObject -Server $dc -SearchBase "CN=ForeignSecurityPrincipals,$($dom.DistinguishedName)" `
  -LDAPFilter "(objectClass=foreignSecurityPrincipal)" -Properties memberOf,objectSid |
  Select-Object name,objectSid,memberOf

# 6. Контекст трастів (read-only)
nltest /domain_trusts /all_trusts /v

# 7. Коли саме змінювався склад — метадані реплікації, read-only
Get-ADReplicationAttributeMetadata -Server $dc -Object $daDN -Properties member -ShowAllLinkedValues |
  Select-Object AttributeName,AttributeValue,LastOriginatingChangeTime,Version

# 8. Якщо модуля ActiveDirectory немає — той самий member через in-box repadmin
repadmin /showattr $dc "CN=Domain Admins,CN=Users,DC=corp,DC=local" /atts:member

Що вона робить: ключ — рядок 1.2.840.113556.1.4.1941 (LDAP_MATCHING_RULE_IN_CHAIN, у MS-ADTS — LDAP_MATCHING_RULE_TRANSITIVE_EVAL): DC проходить ланцюг memberOf угору до кінця, а не на один крок. Різниця між командами 1 і 2 — це і є вкладеність, якої ви не бачили.

Фільтр 1941 і атрибут member не бачать членства за первинною групою — його дає лише окремий запит по primaryGroupID (команда 4), без якого транзитивний склад неповний саме на техніку тихого закріплення.

Get-ADGroupMember -Recursive свідомо не використовується: за документацією Microsoft, якщо в групі є foreign security principal з видаленим чи недосяжним обʼєктом-джерелом, командлет падає з «The server was unable to process the request due to an internal error» або мовчки віддає неповний список — ламається там, де потрібен найбільше. Членів за первинною групою він, утім, повертає — тому команда 4 існує окремо.

Обʼєкти foreignSecurityPrincipal мають імʼя-SID (S-1-5-21-...): розвʼязати в імена можна лише з доступом до домену-джерела, для одностороннього вхідного трасту буде тільки SID.

Команда 3 запускається окремо, а не одним OR-фільтром із командою 2: результат той самий, зате видно, який запит дорогий, і падіння одного не вимагає перезапуску обох.

DC=corp,DC=local у команді 8 замініть на свій. Фільтр 1941 виконується на DC, а не на клієнті — див. правило таймауту в розділі 0.

Мінімальні права: звичайний доменний користувач (Authenticated Users): member, memberOf, objectClass, sAMAccountName, primaryGroupID і конструйований tokenGroups читаються за замовчуванням, nltest /domain_trusts, repadmin /showattr та Get-ADReplicationAttributeMetadata теж працюють від рядового. Domain Admin не потрібен і не має використовуватися: вимога DA для інвентаризації — вже симптом. Виняток — свідомий Deny на читання для нестандартних груп: запит поверне менше рядків, а не помилку, і це теж результат.

Що НЕ змінює: нічого. Усі канали (ADUC у режимі перегляду, domain.msc у режимі перегляду, LDAP-пошук, repadmin /showattr) виконують лише LDAP-операцію search. Жоден із наведених командлетів (Get-ADGroup, Get-ADObject, Get-ADUser, Get-ADComputer) не має параметрів запису й фізично не вміє модифікувати каталог. nltest /domain_trusts — read-only перелік трастів. repadmin /showattr читає атрибути; він не ініціює реплікацію (це роблять інші ключі repadmin, які тут не використовуються). Get-ADReplicationAttributeMetadata читає метадані, не змінюючи їх. tokenGroups обчислюється на льоту й каталогу не змінює — але це дорога операція, див. крок 6. У ADUC: не натискати Add/Remove на вкладці Members і Apply у Properties — виходити через Cancel.

Слід у логах: за замовчуванням читання членства груп через LDAP не залишає жодної події — це треба сказати SOC, щоб не було очікування «ми побачимо, коли аудитор працював».

  • Модуль ActiveDirectory ходить до контролера через ADWS (TCP 9389) звичайним LDAP-пошуком; у журналах — серії 4624 / 4634 з Logon Type 3 на цільовому DC, по парі на виклик командлета.
  • 4662 (An operation was performed on an object), субкатегорія Directory Service Access — ТІЛЬКИ за SACL на конкретному обʼєкті. Аудит читань в AD дуже обʼємний, тому субкатегорію на DC зазвичай тримають вимкненою; якщо Tier 0-групи під SACL — попередьте SOC про сплеск.
  • 1644 у журналі Directory Service — лише при діагностиці «15 Field Engineering» рівня 5+: фільтр 1941 по великому домену може лягти туди як expensive/inefficient search із текстом фільтра, IP клієнта та іменем облікового запису.
  • 4799 (A security-enabled local group membership was enumerated) не зʼявиться взагалі — це подія SAMR, якого модуль ActiveDirectory не використовує. 4799 дають net.exe, Get-LocalGroupMember і SAMR-орієнтовані інструменти; її відсутність не означає, що ніхто не дивився.
  • З Microsoft Defender for Identity можливі алерти Security principal reconnaissance (LDAP) і User and group membership reconnaissance (SAMR); другий має період навчання й реагує на нетипове джерело — разова інвентаризація з нового хоста таким і є.
  • Окремо запросіть у SOC зустрічні дані, яких у каталозі немає: 4728/4729 (глобальна група), 4732/4733 (доменлокальна), 4756/4757 (універсальна).

Що сказати SOC заздалегідь, у їхніх термінах: «LDAP-читання через ADWS (TCP 9389) з хоста X під обліковим записом Y у вікні Z, цільовий контролер — DC02; очікуються серії 4624/4634 Logon Type 3, можливі 1644 при ввімкненій діагностиці; змін не буде».

Якщо ADWS на контролері недоступний і командлети падають — службу не чіпайте, це зміна стану. Перейдіть на ldp.exe / dsquery або узгодьте окремо з власником DC.

Як читати результат:

  • Відніміть вивід команди 1 від команди 2: різниця — ті, хто має Tier 0, але кого немає в жодному вашому списку доменних адмінів. Це головна цифра розділу.
  • Вивід команди 4 додайте окремим рядком: обліковий запис із primaryGroupID=512, якого немає ні в команді 1, ні в команді 2, має повні права DA і нуль присутності у звичайних звітах.
  • objectClass=group — проміжні ланки; випишіть шлях кожної: «X входить у Y, Y входить у Domain Admins». Саме формулювання шляху зазвичай спричиняє рішення.
  • objectClass=computer — компʼютерний обліковий запис із правами доменного адміна: компрометація такого хоста = компрометація домену.
  • Непорожній servicePrincipalName у члена Tier 0 — сервісний акаунт із правами DA. Найвищий пріоритет.
  • objectClass=foreignSecurityPrincipal — зовнішній принципал; якщо ви не можете назвати, хто це, ваша межа довіри проходить не там, де ви думали.
  • Порівняйте Members у Domain Admins і в Builtin\Administrators: це РІЗНІ групи з різним складом, і в другій частіше знаходять забуте.
  • LastOriginatingChangeTime з команди 7 дає дати додавання там, де журналу аудиту немає. Не заміна SIEM, але часто єдине, що лишилося.

Межа методу

Чого цим способом не побачите: це список, а не ланцюг. Ви дізналися, ХТО всередині, і не дізналися, ХТО МОЖЕ туди потрапити — різні множини, і друга майже завжди більша.

  • Права на саму групу. Хто має WriteProperty на member, GenericAll, WriteDacl або є Owner у nTSecurityDescriptor групи, у виводі не зʼявиться жодним рядком — але допише себе туди за секунду. Членство і можливість членства цей запит не розрізняє (див. перевірку 2).
  • Обхідні шляхи, що не проходять через групи взагалі: делеговані ACE на OU, права на обʼєкт користувача-адміна (скидання пароля), RBCD через msDS-AllowedToActOnBehalfOfOtherIdentity на обʼєкті DC, права на GPO, злінкований на Domain Controllers.
  • Транзитивність у зворотний бік: доменна група у локальних Administrators на 200 серверах живе в SAM кожного хоста, а не в AD — LDAP її не бачить.
  • tokenGroups не дає повного складу груп із інших доменів і лісів — той сліпий сектор із FSP, заради якого перевірку й роблять, він не закриває.
  • Foreign security principal лишається SID, якщо доступу до домену-джерела немає: ви бачите зовнішнього принципала в Tier 0 і не бачите, хто це і скільки людей за ним.
  • Гібридний стик. Чи синхронізована група в Entra, чи має її член роль у тенанті, чи веде дорога назад через обліковий запис синхронізації — у виводі нічого (див. перевірку 7).
  • Час. Це знімок на зараз: метадані реплікації дадуть дати останніх змін, але не історію «додали й прибрали через тиждень» — вона тільки в подіях 4728/4732/4756 і 4729/4733/4757, якщо аудит увімкнено і глибина зберігання дозволяє.
  • Пріоритет. Сорок членів у виводі виглядають однаково; який досяжний з робочої станції бухгалтера за два кроки, а який захищений — із цього списку не випливає.

Безкоштовні інструменти, що це автоматизують: усе потрібне вже є в системі та в безкоштовному RSAT — ADUC (dsa.msc), domain.msc, ldp.exe, модуль ActiveDirectory, in-box nltest, repadmin і dsacls. Ліцензії не потрібні. Готовий звіт замість ручного обходу: PingCastle (Netwrix) — health-check домену з розділом про привілейовані групи; ліцензія Non-Profit OSL 3.0 дозволяє використання всередині власної організації, зокрема комерційної, але аудит чужих середовищ як платна послуга потребує комерційної ліцензії. Purple Knight (Semperis) — безкоштовний сканер індикаторів для AD і Entra ID. BloodHound Community Edition (SpecterOps) — безкоштовний і відкритий, будує граф, а не список, тобто закриває частину прогалини вище; ціна — сервер із базою і збирач, який шумить у логах значно більше за кілька LDAP-запитів.

Небезпечні ACL на привілейованих групах: хто може дописати себе в Tier 0

Що це і чому небезпечно

Членство в Tier 0 — це не лише список у вкладці Members, а й перелік тих, хто може його змінити. ACE з GenericAll, WriteDacl, WriteOwner на Domain Admins рівноцінний членству, але в звітах «хто в адмінах» його не видно.

WriteDacl дозволяє переписати DACL і видати собі решту прав. WriteOwner робить власником, а власник за замовчуванням змінює DACL. WriteProperty на атрибут member (schemaIDGUID bf9679c0-0de6-11d0-a285-00aa003049e2) чи на property set Membership (Rights-GUID bc0ac240-79a9-11d0-9020-00c04fc2d4cf) — пряме дописування учасників. Валідований запис Self-Membership (Rights-GUID той самий, bf9679c0-0de6-11d0-a285-00aa003049e2, але маска Self, а не WriteProperty; Display-Name «Add/Remove self as member») додає саме себе. Розширене право User-Force-Change-Password (Rights-GUID 00299570-246d-11d0-a768-00aa006e0529, Display-Name «Reset Password») на обʼєкті користувача скидає пароль без знання старого — захоплення обліковки члена групи без дотику до самої групи.

Ці ACE лишає зазвичай не зловмисник, а стара делегація хелпдеску, інсталятор, який попросив «трохи прав», або успадкування з OU, налаштованого у 2016 році.

Як подивитися вручну

  1. На станції з RSAT відкрийте ADUC (dsa.msc).
  2. View → Advanced Features: без цього вкладки Security не буде.
  3. Домен → контейнери Builtin і Users.
  4. Права кнопка на Domain Admins → Properties → SecurityAdvanced.
  5. Вкладка Permissions: колонки Principal / Access / Inherited from / Applies to; клік по Access групує рядки.
  6. Рядки-мішені: Full control, Modify permissions, Modify owner, Write members, Add/Remove self as member, Reset password. Виписуйте всі, де Principal не Domain Admins / Enterprise Admins / Administrators / SYSTEM / Account Operators (типові успадковані).
  7. Inherited from: «(none)» = ACE поставили тут; DN якогось OU = проблема ширша за групу, дивіться той OU (перевірка 3).
  8. Вкладка Owner: власник не Domain Admins/Administrators — окрема знахідка.
  9. Вкладка Effective Access: Select a user → принципал → View effective access — права з урахуванням вкладеності.
  10. Кроки 4–9 — для Enterprise Admins, Schema Admins, Administrators, Account Operators, Backup Operators, Server Operators, Print Operators, Domain Controllers, Key Admins, Enterprise Key Admins, DnsAdmins і власних груп на кшталт «SRV-Admins».
  11. Те саме на обʼєктах користувачів — членах Tier 0 з перевірки 1, але рядки інші: Reset password, Full control, Write all properties, Change password, All extended rights. Скидання пароля доменного адміністратора еквівалентне членству в його групі й у звітах про членство не видно.
  12. Те саме на обʼєктах компʼютерів контролерів домену (OU Domain Controllers): Full control, Write all properties, Validated write to service principal name, будь-який запис у msDS-AllowedToActOnBehalfOfOtherIdentity (перевірка 4).
  13. Закривайте кожне вікно через Cancel, а не OK. Кнопка OK у Advanced Security Settings застосовує зміни навіть тоді, коли ви нічого свідомо не міняли, — достатньо випадкового кліку по чекбоксу успадкування.

Команда

PowerShell
# Спершу — ПРЕАМБУЛА з розділу 0 ($dom, $dc, $protected).

$risky = 'GenericAll','GenericWrite','WriteDacl','WriteOwner','WriteProperty','Self','ExtendedRight'

# 1. Явні та успадковані Allow-ACE з правами на запис — по всіх привілейованих групах
$aclFindings = $protected | ForEach-Object {
    $dn = $_.DistinguishedName
    $sd = (Get-ADObject -Server $dc -Identity $dn -Properties nTSecurityDescriptor).nTSecurityDescriptor
    $sd.Access |
      Where-Object { $_.AccessControlType -eq 'Allow' } |
      Where-Object { (($_.ActiveDirectoryRights -split ',\s*') | Where-Object { $_ -in $risky }).Count -gt 0 } |
      Select-Object @{n='Object';e={$dn}},
                    @{n='Principal';e={$_.IdentityReference.ToString()}},
                    ActiveDirectoryRights,
                    @{n='ObjectTypeGuid';e={$_.ObjectType.ToString()}},
                    IsInherited,
                    @{n='InheritedFrom';e={$_.InheritanceType.ToString()}}
}
$aclFindings | Select-Object Object,Principal,ActiveDirectoryRights,ObjectTypeGuid,IsInherited

# 2. Власники привілейованих груп. Owner може переписати DACL незалежно від його вмісту.
$protected | Select-Object Name,
  @{n='Owner';e={ (Get-ADObject -Server $dc -Identity $_.DistinguishedName -Properties nTSecurityDescriptor).nTSecurityDescriptor.Owner }}

# 3. Розшифровка GUID у колонці ObjectTypeGuid — які саме атрибути й розширені права
#    00000000-0000-0000-0000-000000000000  усі властивості / усе одразу
#    bf9679c0-0de6-11d0-a285-00aa003049e2  member (WriteProperty) / Self-Membership (Self)
#    bc0ac240-79a9-11d0-9020-00c04fc2d4cf  property set Membership
#    00299570-246d-11d0-a768-00aa006e0529  Reset Password (User-Force-Change-Password)
#    ab721a53-1e2f-11d0-9819-00aa0040529b  Change Password (потребує старого пароля)
#    f3a64788-5306-11d1-a9c5-0000f80367c1  Validated write to SPN
#    3f78c3e5-f79a-46bd-a0b8-9d18116ddc79  msDS-AllowedToActOnBehalfOfOtherIdentity (RBCD)

# 4. ACL обʼєктів-користувачів, які є членами Tier 0 (список — з перевірки 1)
$tier0Users = @("CN=Ivan Petrenko,OU=Admins,DC=corp,DC=local")   # підставте свій
$tier0Users | ForEach-Object { dsacls $_ }

# 5. Текстовий ACL однієї групи для звіту або для diff між знімками
dsacls "CN=Domain Admins,CN=Users,DC=corp,DC=local"

# 6. Вивантаження результату для подальшого зіставлення (файл на вашій машині)
$aclFindings | Export-Csv -NoTypeInformation -Encoding UTF8 -Path "$HOME\Desktop\tier0-acl.csv"

Що вона робить: читає nTSecurityDescriptor кожної привілейованої групи і лишає Allow-ACE з хоча б одним правом на запис із переліку. ReadProperty і ListChildren нікуди не ведуть і в результат не потрапляють.

IsInherited — найважливіша колонка і найчастіше пропущена. False = ACE поставили на цей обʼєкт свідомо; True = право прийшло з батьківського контейнера і живе на OU (перевірка 3). Виправляють їх у різних місцях.

ObjectTypeGuid уточнює загальне «WriteProperty» до «WriteProperty на атрибут member». Порожній GUID (00000000-...) — «на всі властивості», тобто GenericWrite. У блоці 3 не повний перелік, а ті GUID, які ведуть до Tier 0.

dsacls без ключів /G, /R, /D, /S, /P, /I лише друкує ACL: найпростіша базова лінія без жодного інструмента. Export-Csv пише файл на вашій машині, а не в каталог; тримайте його там, де решту матеріалів обмеженого доступу: це опис поверхні атаки.

Мінімальні права: звичайний доменний користувач. Читання DACL потребує READ_CONTROL зі стандартного Read для Authenticated Users; Effective Access у ADUC рахує результат локально з прочитаного дескриптора. SACL (частина аудиту) без SeSecurityPrivilege не віддадуть, але для цієї перевірки він не потрібен. Domain Admin не потрібен і не має використовуватися.

Одне чесне обмеження: явний Deny на читання дескриптора для вашої групи дасть менше рядків, а не помилку. Обʼєкт, ACL якого ви не змогли прочитати, — це окремий рядок у ваших нотатках, а не «чисто».

Що НЕ змінює: нічого. Get-ADObject, Select-Object, Where-Object, ForEach-Object не мають режиму запису. dsacls без ключів модифікації лише друкує. Export-Csv пише локальний файл.

У ADUC небезпечна не команда, а кнопка: у вікні Advanced Security Settings є чекбокс успадкування і кнопка Restore defaults, і випадковий клік по будь-якому з них плюс OK — це зміна ACL привілейованої групи. Тому в цій перевірці вікна закриваються тільки через Cancel. Вкладка Effective Access нічого не записує — вона лише обчислює.

Слід у логах:

  • Читання дескрипторів через LDAP — звичайний search атрибута nTSecurityDescriptor. За замовчуванням слідів немає, окрім 4624/4634 Logon Type 3 на цільовому DC.
  • 4662 зʼявиться, якщо на групі налаштовано SACL. У зрілих середовищах його на Domain Admins і на AdminSDHolder ставлять свідомо: тоді вашу перевірку буде видно, і це добре — попередьте заздалегідь.
  • 1644 — якщо ввімкнено діагностику «15 Field Engineering»: десяток обʼєктів туди навряд чи потрапить, масове читання дескрипторів по всьому домену (перевірка 3) — майже напевно.
  • Microsoft Defender for Identity реагує на масове читання дескрипторів безпеки сигналом Security principal reconnaissance (LDAP): телеметрично ваш аудит і перша фаза атаки збігаються.

Що сказати SOC: «читання nTSecurityDescriptor привілейованих груп і обʼєктів-адміністраторів, LDAP через ADWS, хост X, обліковий запис Y, вікно Z, цільовий DC; змін не буде».

Як читати результат:

  • Спершу відсортуйте за IsInherited = False. Явний ACE — рішення конкретної людини в конкретний день; кожен має мати пояснення.
  • Principal, якого ви не впізнаєте, важливіший за право, яке ви впізнали. Нерозвʼязаний SID (S-1-5-21-... без імені) означає: принципала немає, а право живе — і повернеться з обʼєктом, що отримає той самий SID.
  • WriteDacl і WriteOwner читаються як GenericAll із затримкою на один крок — не помʼякшуйте їх у звіті.
  • Self на bf9679c0-... («Add/Remove self as member») виглядає невинно і не є ним. На групі Tier 0 для широкої групи на кшталт «All IT» це стільки доменних адміністраторів, скільки в тій групі людей.
  • Reset password на обʼєкті адміністратора — окремий пріоритет: право не на групу, тому в перевірці 1 його не було видно, а результат той самий.
  • Owner з команди 2: власник, який не Domain Admins і не Administrators, — найкоротший рядок звіту і найдовша розмова.
  • Порівняйте вивід dsacls між двома датами: різниця — те, що змінилося без вашого відома.

Межа методу

Чого цим способом не побачите: ви побачили прямі права на десяток конкретних обʼєктів. Ланцюг починається там, де закінчується ця перевірка.

  • Транзитивності немає. Get-Acl і dsacls покажуть, що принципал X має WriteDacl на групу A, але не те, що X — вкладена група, куди входить група B, на яку хтось має GenericAll з третього місця. Кожен крок читається окремо; композиція — ні.
  • Ефективні права для всіх принципалів одразу не обчислюються. Effective Access працює по одному принципалу за раз: на сотні принципалів це сотні кліків.
  • Обʼєкти поза списком не перевірені. Групу «SRV-Admins», яка через вкладеність веде туди ж, ви перевірите тільки якщо самі про неї згадали.
  • Права на батьківські контейнери не покриті. WriteDacl на OU, де лежить група, дає ті самі можливості, а в ACL самої групи не зʼявиться жодним рядком (перевірка 3).
  • Немає часу. Коли цей ACE поставили і хто — без SACL і без подій 5136 (A directory service object was modified) відповіді немає. Метадані реплікації дадуть дату останньої зміни nTSecurityDescriptor, але не автора і не попереднє значення.
  • Немає пріоритету. Тридцять ACE у звіті виглядають однаково; який із них лежить на маршруті, яким реально можна пройти з робочої станції рядового користувача, з таблиці не випливає.
  • Гібридного боку немає взагалі: права на групу в AD нічого не кажуть про те, чи має ця група роль у Entra.

Безкоштовні інструменти, що це автоматизують: RSAT — ADUC (вкладки Security, Owner, Effective Access), ldp.exe, in-box dsacls.exe. AD ACL Scanner — безкоштовний відкритий PowerShell-інструмент: знімає ACL по гілці, HTML-звіт, diff між знімками, фільтр за типом права; для контролю базової лінії практичніший за ручне зіставлення dsacls. PingCastle Community має правила щодо нестандартних ACE на привілейованих обʼєктах (умови ліцензії — перевірка 1). Purple Knight — індикатори небезпечних дозволів. BloodHound CE перетворює ці самі ACE на ребра WriteDacl, WriteOwner, AddMember, ForceChangePassword і відповідає на питання «покажи найкоротший шлях звідси до Domain Admins», якого решта інструментів не ставить узагалі.

Частина II. Шляхи повз членство

Делеговані права поза стандартними контейнерами: ACL на OU, на корені домену і DCSync

Що це і чому небезпечно

Перевірка 2 дивилася на десяток відомих обʼєктів. Ця — у протилежний бік: де взагалі у вашому каталозі роздані права на запис і кому. Найчастіше знаходять три речі.

Перше — делегування на OU, яке пережило свою причину. Full control на OU, виданий групі підрядника у 2016-му під міграцію, живе й після неї — і, успадковуючись вниз, діє на кожен обʼєкт, створений у цій OU відтоді, включно з сервісними акаунтами, покладеними туди через пʼять років.

Друге — права на корені домену. ACE на самому обʼєкті DC=corp,DC=local успадковується всюди, де успадкування не заблоковане. Тут і живе найкоротший шлях: DS-Replication-Get-Changes (1131f6aa-9c07-11d1-f79f-00c04fc2dcd2) і DS-Replication-Get-Changes-All (1131f6ad-9c07-11d1-f79f-00c04fc2dcd2) разом дають запросити в контролера домену репліку будь-якого обʼєкта із секретами — хеші паролів усього домену, включно з krbtgt, не заходячи на жоден сервер. Штатно ці права мають контролери домену, Domain Admins, Enterprise Admins і обліковий запис синхронізації Entra Connect (перевірка 7). Будь-хто ще в цьому списку — це те, заради чого написана вся методичка.

Третє — контейнери, на які ніхто не дивиться. CN=System, CN=Program Data, CN=Managed Service Accounts, а також Configuration NC: CN=Sites (звідки лінкуються GPO на цілі сайти) і CN=Services. Технічно це не OU, тож у типові звіти по OU вони не потрапляють.

Як подивитися вручну

  1. ADUC (dsa.msc) → View → Advanced Features.
  2. Правою на самому домені → Properties → Security → Advanced — головне вікно перевірки. Виписати ACE з Principal поза стандартним набором (SYSTEM, SELF, Authenticated Users, Enterprise Domain Controllers, Administrators, Domain Admins, Enterprise Admins, Pre-Windows 2000 Compatible Access).
  3. Там же — Access = Replicating Directory Changes і Replicating Directory Changes All: виписати кожного принципала поіменно; список має бути коротким.
  4. Для кожної OU: правою → Properties → Security → Advanced; колонки Principal, Access, Applies to, Inherited from. Applies to = Descendant User objects разом із Full control — делегування на всіх користувачів піддерева.
  5. Кнопка Disable inheritance означає, що успадкування зараз увімкнене: зафіксуйте і не натискайте. OU із заблокованим успадкуванням — окремий список.
  6. Те саме для CN=System, CN=Program Data, CN=Managed Service Accounts, CN=Users, CN=Computers (видно лише з Advanced Features).
  7. Configuration NC: adsiedit.mscConfigurationCN=Sites → Properties → Security → Advanced.
  8. dssite.msc — чи є на сайтах злінковані GPO взагалі (наочніше — Group Policy Inheritance у GPMC для сайту).
  9. Для підозрілого принципала — Effective Access на OU: що він фактично може з піддеревом.
  10. Усі вікна закривати через Cancel.

Команда

PowerShell
# Спершу — ПРЕАМБУЛА з розділу 0 ($dom, $dc).
# УВАГА: це найважчий запит методички. Перший запуск — поза робочими годинами.

$risky = 'GenericAll','GenericWrite','WriteDacl','WriteOwner','WriteProperty','CreateChild','DeleteChild','ExtendedRight','Self'

# Базова лінія принципалів, які тут мають бути. Перегляньте її під свій домен,
# перш ніж покладатися на фільтр: усе, що ви сюди впишете, зникне зі звіту.
$nb = $dom.NetBIOSName
$baseline = @(
  'NT AUTHORITY\SYSTEM','NT AUTHORITY\SELF','NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS',
  'BUILTIN\Administrators','BUILTIN\Pre-Windows 2000 Compatible Access',
  "$nb\Domain Admins","$nb\Enterprise Admins","$nb\Domain Controllers",
  "$nb\Enterprise Read-only Domain Controllers","CREATOR OWNER"
)

# 1. Цілі: корінь домену + усі OU + системні контейнери першого рівня
$targets  = @()
$targets += Get-ADObject -Server $dc -Identity $dom.DistinguishedName -Properties nTSecurityDescriptor
$targets += Get-ADOrganizationalUnit -Server $dc -Filter * -Properties nTSecurityDescriptor
$targets += Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName -SearchScope OneLevel `
              -LDAPFilter "(objectClass=container)" -Properties nTSecurityDescriptor

# 2. Явні (не успадковані) Allow-ACE з правами на запис, поза базовою лінією
$delegations = $targets | ForEach-Object {
    $dn = $_.DistinguishedName
    $_.nTSecurityDescriptor.Access |
      Where-Object { $_.AccessControlType -eq 'Allow' -and -not $_.IsInherited } |
      Where-Object { $_.IdentityReference.ToString() -notin $baseline } |
      Where-Object { (($_.ActiveDirectoryRights -split ',\s*') | Where-Object { $_ -in $risky }).Count -gt 0 } |
      Select-Object @{n='Container';e={$dn}},
                    @{n='Principal';e={$_.IdentityReference.ToString()}},
                    ActiveDirectoryRights,
                    @{n='ObjectTypeGuid';e={$_.ObjectType.ToString()}},
                    @{n='AppliesToGuid';e={$_.InheritedObjectType.ToString()}},
                    InheritanceType
}
$delegations | Select-Object Container,Principal,ActiveDirectoryRights,InheritanceType

# 3. DCSync: розширені права реплікації на корені домену. Найкоротший рядок звіту.
$replGuids = @(
  '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2',  # DS-Replication-Get-Changes
  '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2',  # DS-Replication-Get-Changes-All
  '89e95b76-444d-4c62-991a-0facbeda640c'   # DS-Replication-Get-Changes-In-Filtered-Set
)
$rootSd = (Get-ADObject -Server $dc -Identity $dom.DistinguishedName -Properties nTSecurityDescriptor).nTSecurityDescriptor
$rootSd.Access |
  Where-Object { $_.ObjectType.ToString() -in $replGuids } |
  Select-Object @{n='Principal';e={$_.IdentityReference.ToString()}},ActiveDirectoryRights,
                @{n='Right';e={$_.ObjectType.ToString()}},IsInherited

# 4. OU із заблокованим успадкуванням — там ваша модель делегування не діє
Get-ADOrganizationalUnit -Server $dc -Filter * -Properties nTSecurityDescriptor |
  Where-Object { $_.nTSecurityDescriptor.AreAccessRulesProtected } |
  Select-Object Name,DistinguishedName

# 5. Configuration NC: сайти (звідки лінкуються GPO на цілі майданчики)
$confNC = (Get-ADRootDSE -Server $dc).configurationNamingContext
dsacls "CN=Sites,$confNC"

# 6. Текстовий зріз кореня домену для базової лінії і майбутнього diff
dsacls "DC=corp,DC=local"

# 7. Вивантаження (файл на вашій машині)
$delegations | Export-Csv -NoTypeInformation -Encoding UTF8 -Path "$HOME\Desktop\ou-delegations.csv"

Що вона робить: команда 1 збирає цілі — корінь домену, всі OU і контейнери першого рівня; команда 2 лишає з їхніх дескрипторів лише Allow, явні (IsInherited = False), з правами на запис, поза базовою лінією.

Фільтр $baseline — найвідповідальніший рядок перевірки: надто широка базова лінія перетворює аудит на самозаспокоєння. Перегляньте її під свій домен явно.

Колонка AppliesToGuid (InheritedObjectType) каже, на які класи обʼєктів це діє: порожній GUID = на всі, GUID класу user (bf967aba-0de6-11d0-a285-00aa003049e2) = на всіх користувачів піддерева, тобто на всіх людей у гілці, а не на один обʼєкт.

Команду 3 читайте першою: штатних власників цих прав перелічено вище, кожен інший рядок = хеші паролів усього домену штатною реплікацією, без входу на сервер і без «експлойта». Команда 4 дає OU із заблокованим успадкуванням — не дефект, але місця, де уявна модель делегування розходиться з фактичною.

Export-Csv пише локальний файл із картою делегування вашого домену — поводьтеся з ним відповідно.

Мінімальні права: звичайний доменний користувач. Читання DACL контейнерів і OU потребує READ_CONTROL зі стандартного Read; Get-ADRootDSE і Configuration NC доступні Authenticated Users.

Два чесні обмеження: SACL не читається без SeSecurityPrivilege (тут він не потрібен), а CN=Deleted Objects потребує окремо делегованого права і сюди свідомо не входить — видалені обʼєкти разом зі своїми ACE лишаться поза картиною.

Що НЕ змінює: нічого. Усі командлети — читання; dsacls без ключів модифікації — друк; Export-Csv — локальний файл.

Ризик у ADUC і ADSI Edit той самий, що в перевірці 4, і вищий: в Advanced Security Settings на OU випадковий клік по чекбоксу успадкування плюс OK змінює модель доступу до цілого піддерева. Правило те саме: Cancel, завжди. Майстра Delegation of Control не відкривайте навіть «щоб подивитися» — він завершується записом.

Слід у логах:

  • Найпомітніша перевірка методички: масове читання nTSecurityDescriptor по всіх OU телеметрично найближче до SharpHound.
  • 1644 у Directory Service — за ввімкненої діагностики зʼявиться майже напевно: широкий пошук по доменному NC із запитом дескрипторів підпадає під критерій дорогого запиту.
  • 4662 — на кожному обʼєкті з налаштованим SACL. Корінь домену під SACL тримають частіше, ніж OU.
  • 4624/4634 Logon Type 3 на цільовому DC — серіями.
  • Microsoft Defender for Identity: Security principal reconnaissance (LDAP) — ймовірність висока; це той патерн, який сигнал і має ловити.
  • 5145 — якщо в тому ж вікні дивитиметеся GPO в SYSVOL.

SOC тут попереджають до, а не «взагалі на тижні»: конкретне вікно, хост, обліковий запис, цільовий DC і окремим рядком — «читання дескрипторів безпеки по всіх OU і кореню домену».

Як читати результат:

  1. Спершу — команда 3. Принципал поза очікуваними чотирма категоріями — і решта звіту може почекати.
  2. Рядки з Applies to = Descendant User objects або порожнім AppliesToGuid разом із GenericAll — делегування на все піддерево. Питання до кожного: хто вирішив і чи причина ще існує.
  3. CreateChild на клас computer — можливість заводити машини в домен у цій OU; разом із делегованим правом писати SPN це половина сценарію з делегуванням (перевірка 4).
  4. Нерозвʼязані SID — те саме, що й у перевірці 4: право живе, власника немає.
  5. OU із заблокованим успадкуванням (команда 4).
  6. Два числа для аркуша екстраполяції розділу 13: скільки унікальних принципалів має хоч одне явне право (B) і на скількох обʼєктах у середньому (C) — усередніть десяток випадкових, потрібен порядок величини, не точність.

Межа методу

Чого цим способом не побачите: ви отримали карту прямих делегувань. Маршрутів на ній немає.

  • Композиція прав через вкладеність груп не рахується. Якщо принципал у звіті — це група, куди входить інша група з невідомими вам членами, рядок звіту описує двадцять людей замість одного, і ви цього з таблиці не бачите.
  • Успадковані ACE свідомо відфільтровані. Це правильно для пошуку рішень людей, але фактичних прав на конкретному обʼєкті внизу дерева ви не бачите — для цього треба дивитися ефективний доступ на самому обʼєкті.
  • Ефективні права не обчислені. Deny-ACE, порядок обробки, вкладеність — усе це вікно Effective Access рахує по одному принципалу за раз.
  • Контейнери, яких ви не перерахували, не перевірені. Схема (Schema NC), CN=Deleted Objects, DNS-зони в CN=MicrosoftDNS, обʼєкти AD CS у Configuration NC (шаблони сертифікатів — окрема і велика тема) сюди не входять.
  • Файлова частина не покрита взагалі: дозволи NTFS на SYSVOL і на шарах, локальні групи адміністраторів на серверах, права на реєстр — це не каталог, і LDAP їх не бачить.
  • Немає часу і автора. Коли ACE зʼявився і хто його поставив, без SACL і подій 5136 невідомо.
  • Немає пріоритету і немає choke points. Якщо у вас 400 рядків, який із них лежить на перетині сорока маршрутів — на списку принципово не видно. Це і є межа, до якої повертається розділ 13.

Безкоштовні інструменти, що це автоматизують: RSAT — ADUC, ADSI Edit, dssite.msc, ldp.exe; in-box dsacls.exe. AD ACL Scanner створений рівно для цієї задачі: обійти гілку, зняти ACL, дати HTML-звіт із розшифрованими GUID і порівняти два знімки. PingCastle Community має правила щодо делегувань і прав реплікації (умови ліцензії — див. перевірку 1). Purple Knight — індикатори щодо DCSync і небезпечних делегувань. Microsoft Entra ID Recommendations у preview має пункт Remove non-admin accounts with DCsync permissions, але живиться даними Defender for Identity — без сенсорів на DC не зʼявиться (розділ 14). BloodHound CE перетворює ці ACE на ребра GenericAll, WriteDacl, DCSync і будує з них шляхи.

Делегування Kerberos: unconstrained, constrained, protocol transition і RBCD

Що це і чому небезпечно

Делегування дозволяє службі діяти від імені користувача — без нього не працює дволанкова архітектура. Небезпека — у різниці між трьома його поколіннями.

Unconstrained (необмежене) делегування. Прапорець TRUSTED_FOR_DELEGATION (0x80000, десяткове 524288) у userAccountControl. Хто автентифікується на такому хості, лишає в його памʼяті свій TGT — повний квиток для дій від свого імені будь-де. На контролерах домену це штатно й правильно, будь-де ще — майже завжди спадщина.

Constrained (обмежене) делегування. Атрибут msDS-AllowedToDelegateTo перелічує конкретні SPN, до яких служба може діяти від імені користувача. Обмежено службу-ціль, а не користувача: компрометація такого акаунта дає імперсонацію будь-кого (крім захищених від делегування) саме до цих служб.

Protocol transition. Прапорець TRUSTED_TO_AUTH_FOR_DELEGATION (0x1000000, десяткове 16777216), у ADUC — «Use any authentication protocol». Із ним квиток користувача службі не потрібен взагалі: вона запросить його сама (S4U2Self) для будь-кого. Тобто «можу стати ким завгодно до цих служб, без участі того, ким стаю».

Resource-based constrained delegation (RBCD). Атрибут msDS-AllowedToActOnBehalfOfOtherIdentity — дескриптор безпеки на цільовому ресурсі: кому дозволено діяти до нього від імені інших. Виставляє його той, хто має право писати в атрибути обʼєкта, тож GenericWrite на компʼютер (перевірка 2/5) плюс можливість завести машину в домен дають повний доступ до нього. Найтихіший із чотирьох механізмів і найрідше інвентаризований.

Захист теж читається з каталогу: прапорець NOT_DELEGATED (0x100000, «Account is sensitive and cannot be delegated») і членство в Protected Users.

Як подивитися вручну

  1. ADUC (dsa.msc) → View → Advanced Features.
  2. Вкладка Delegation зʼявляється лише за наявності SPN. Не бачите її — це не помилка прав; дивіться userAccountControl в Attribute Editor.
  3. На вкладці Delegation три перемикачі:

    • Do not trust this computer for delegation — норма;
    • Trust this computer for delegation to any service (Kerberos only) — unconstrained;
    • Trust this computer for delegation to specified services only — constrained; під ним Use Kerberos only і Use any authentication protocol (це і є protocol transition).
  4. OU Domain Controllers: тут unconstrained очікуваний. Звірте з фактичним переліком контролерів домену — обʼєкт з unconstrained поза цим переліком і є вашою знахідкою.
  5. Далі — OU рядових серверів і обʼєкти-користувачі: сервісні акаунти теж бувають довіреними для делегування.
  6. RBCD в ADUC не видно — атрибут бінарний, вкладки для нього немає. Дивіться в adsiedit.msc → обʼєкт → Properties → Attribute Editor → msDS-AllowedToActOnBehalfOfOtherIdentity: <Not set> або бінарні дані, які ADSI Edit покаже як дескриптор по кнопці перегляду. SID зручніше розшифрувати командою (нижче).
  7. Захист: ADUC → обліковий запис адміністратора → Account → Account options → Account is sensitive and cannot be delegated. Виписати, у кого з Tier 0 його немає.
  8. ADUC → Users → група Protected Users → Members. Порожньо — теж відповідь.
  9. Усі вікна закривати через Cancel.

Команда

PowerShell
# Спершу — ПРЕАМБУЛА з розділу 0 ($dom, $dc).

# 1. UNCONSTRAINED: прапорець TRUSTED_FOR_DELEGATION = 0x80000 = 524288.
#    Контролери домену тут очікувані — звіряйте з фактичним переліком DC.
Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName `
  -LDAPFilter "(&(|(objectCategory=computer)(objectCategory=person))(userAccountControl:1.2.840.113556.1.4.803:=524288))" `
  -Properties sAMAccountName,objectClass,userAccountControl,primaryGroupID,operatingSystem,lastLogonTimestamp |
  Select-Object sAMAccountName,objectClass,operatingSystem,
    @{n='IsDC';e={ $_.primaryGroupID -in 516,521 }},DistinguishedName

# 2. CONSTRAINED: непорожній msDS-AllowedToDelegateTo
Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName `
  -LDAPFilter "(msDS-AllowedToDelegateTo=*)" `
  -Properties sAMAccountName,objectClass,userAccountControl,msDS-AllowedToDelegateTo |
  Select-Object sAMAccountName,objectClass,
    @{n='DelegatesTo';e={ $_.'msDS-AllowedToDelegateTo' -join '; ' }},
    @{n='ProtocolTransition';e={ [bool]($_.userAccountControl -band 0x1000000) }},
    DistinguishedName

# 3. PROTOCOL TRANSITION окремо: TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000 = 16777216
Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName `
  -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=16777216)" `
  -Properties sAMAccountName,objectClass,msDS-AllowedToDelegateTo |
  Select-Object sAMAccountName,objectClass,@{n='DelegatesTo';e={ $_.'msDS-AllowedToDelegateTo' -join '; ' }},DistinguishedName

# 4. RBCD: хто кому дозволив діяти від свого імені. Атрибут — бінарний дескриптор.
Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName `
  -LDAPFilter "(msDS-AllowedToActOnBehalfOfOtherIdentity=*)" `
  -Properties sAMAccountName,objectClass,msDS-AllowedToActOnBehalfOfOtherIdentity |
  ForEach-Object {
      $target = $_.DistinguishedName
      $raw = $_.'msDS-AllowedToActOnBehalfOfOtherIdentity'
      $sd  = [System.Security.AccessControl.RawSecurityDescriptor]::new($raw, 0)
      $sd.DiscretionaryAcl | ForEach-Object {
          $sid = $_.SecurityIdentifier.Value
          $who = Get-ADObject -Server $dc -LDAPFilter "(objectSid=$sid)" -Properties sAMAccountName -ErrorAction SilentlyContinue
          [pscustomobject]@{
              Target       = $target
              AllowedSID   = $sid
              AllowedName  = $who.sAMAccountName
          }
      }
  }

# 5. Захист: хто з Tier 0 позначений як несхильний до делегування (NOT_DELEGATED = 0x100000)
Get-ADUser -Server $dc -LDAPFilter "(adminCount=1)" -Properties userAccountControl |
  Select-Object sAMAccountName,@{n='SensitiveNotDelegated';e={ [bool]($_.userAccountControl -band 0x100000) }}

# 6. Група Protected Users: склад (порожньо — теж відповідь)
Get-ADGroup -Server $dc -Identity "$dSid-525" -Properties member |
  Select-Object Name,@{n='Members';e={ $_.member.Count }}

# 7. Текстовий зріз атрибутів одного обʼєкта без модуля AD
repadmin /showattr $dc "CN=SRV-APP01,OU=Servers,DC=corp,DC=local" /atts:userAccountControl,msDS-AllowedToDelegateTo

Що вона робить: фільтри 1, 3 і 5 спираються на правило порівняння бітів 1.2.840.113556.1.4.803 (LDAP_MATCHING_RULE_BIT_AND): контролер перевіряє конкретний біт у userAccountControl, а не число цілком. Прапорців там десяток, тож порівняння зі значенням дасть неправильний результат.

Колонка IsDC у команді 1 — первинна група Domain Controllers (516) або Read-only Domain Controllers (521). Усе, де IsDC = False, потребує пояснення.

Команда 4 — єдине місце в методичці, де ми розбираємо вміст атрибута: конструктор RawSecurityDescriptor розкладає бінарний дескриптор у памʼяті клієнта на список ACE, з кожного беремо SID і резолвимо в імʼя окремим читанням. Каталог не змінюється.

Група Protected Users має доменно-відносний RID 525; її члени не можуть бути делеговані, для них вимкнено NTLM, RC4 і кешування облікових даних. Найдешевший захист із наявних.

repadmin /showattr у команді 7 — запасний шлях без модуля AD; ключ лише читає атрибути.

Мінімальні права: звичайний доменний користувач — userAccountControl, msDS-AllowedToDelegateTo і msDS-AllowedToActOnBehalfOfOtherIdentity читаються Authenticated Users за замовчуванням: перелік цілей делегування доступний кожному в домені, як і перелік SPN. Domain Admin і доступ до самих серверів не потрібні.

Що НЕ змінює: нічого — усі командлети, RawSecurityDescriptor над копією байтів і repadmin /showattr тільки читають.

Найризикованіше місце — вкладка Delegation у ADUC: випадковий клік по одній із трьох радіокнопок плюс OK змінює модель делегування на обʼєкті. Дивіться, який перемикач активний, і закривайте вікно через Cancel. Те саме стосується прапорця Account is sensitive and cannot be delegated на вкладці Account.

Слід у логах:

  • LDAP-читання атрибутів: 4624/4634 Logon Type 3. Фільтри по бітах userAccountControl виконуються на DC й індексуються погано — на великому домені можливий 1644 за ввімкненої діагностики.
  • 4662 — лише за наявності SACL.
  • Microsoft Defender for Identity має окрему posture-оцінку щодо небезпечних делегувань; ваше читання підпадає під Security principal reconnaissance (LDAP).
  • Зустрічні дані від SOC: 4738 (A user account was changed) і 5136 (A directory service object was modified) по атрибутах msDS-AllowedToActOnBehalfOfOtherIdentity і userAccountControl за останні місяці. RBCD виставляють записом в атрибут, тож 5136 — єдине місце, де це видно, і саме його зазвичай не збирають.
  • 4769 із незвично широким набором цілей від одного джерела — ознака зловживання делегуванням; це теж запит до SOC, а не ваш слід.

Як читати результат:

  • Команда 1, рядки з IsDC = False. Питання до власника: чи потрібно це досі і чи можна перейти на constrained. Стара ОС у колонці operatingSystem робить рядок терміновим.
  • Команда 2, колонка ProtocolTransition = True. Окремий, третій клас, а не підвид constrained.
  • DelegatesTo зі SPN контролера домену (ldap/, cifs/, host/ на DC) — маршрут до Tier 0 в один крок; найвищий пріоритет усього розділу.
  • Команда 4. Дивіться не на ціль, а на AllowedName: незнайомий компʼютер чи акаунт — зʼясуйте, хто і коли його вписав (подія 5136). RBCD не від вашої команди — це інсталятор продукту або слід.
  • Команда 5. Порожні SensitiveNotDelegated у членів Tier 0 означають, що їхні квитки можуть осісти на будь-якому хості з unconstrained; перетин із виводом команди 1 і є практичний ризик.
  • Команда 6. Нуль членів у Protected Users при непорожній команді 1 — найчастіша комбінація і найдешевша для виправлення.
  • Порахуйте загальну кількість обʼєктів з делегуванням будь-якого типу — це число E для аркуша екстраполяції в розділі 13.

Межа методу

Чого цим способом не побачите: ви знайшли, де механізм увімкнено, а не чи ним хтось скористався і чи веде він кудись.

  • Хто фактично автентифікується на цих хостах. Unconstrained небезпечний лише тоді, коли туди заходить хтось цінний. Каталог не знає, хто і коли туди заходив, — це журнали входів на самому хості (4624) і, частково, 4768/4769 на DC.
  • Що зараз лежить у памʼяті. Живі TGT на сервері, Credential Guard і LSA Protection, рівень оновлень — усе це поза каталогом.
  • Чи потрібне це делегування бізнесу. Найчастіша відповідь на «навіщо тут unconstrained» — «так налаштував інсталятор у 2014». Каталог цієї історії не зберігає.
  • Ланцюг. «Компрометація цього сервера дає квиток адміністратора, який дає доступ до цієї бази, у якій лежить...» — це маршрут, а не атрибут. Ви бачите вузли.
  • Хто МОЖЕ виставити RBCD. Хто має GenericWrite/WriteProperty на обʼєкти компʼютерів — це перевірки 2 і 3; зʼєднувати їх із цим списком доводиться вручну, а саме це зʼєднання і є реальним ризиком RBCD.
  • Квота на створення компʼютерів. Атрибут ms-DS-MachineAccountQuota (за замовчуванням 10) визначає, чи може рядовий користувач завести машину в домен — ключовий інгредієнт сценарію RBCD. Читається одним запитом окремо: Get-ADObject -Identity $dom.DistinguishedName -Properties ms-DS-MachineAccountQuota -Server $dc. Типове значення 10 плюс непорожній вивід цієї перевірки — готовий сценарій RBCD.
  • Делегування в бік інших лісів і поведінка при SID filtering — окрема тема, яку атрибути обʼєкта не описують.

Безкоштовні інструменти, що це автоматизують: RSAT — ADUC (вкладка Delegation), ADSI Edit (для msDS-AllowedToActOnBehalfOfOtherIdentity), ldp.exe. PingCastle Community — правила щодо unconstrained delegation і щодо делегувань на DC (умови ліцензії — див. перевірку 1). Purple Knight — індикатори по всіх чотирьох типах делегування. Microsoft Entra ID Recommendations має preview-пункт Modify unsecure Kerberos delegations to prevent impersonation на даних Defender for Identity — без сенсорів на DC він не зʼявиться (розділ 14). BloodHound CE будує ребра AllowedToDelegate і AllowedToAct і показує, куди делегування веде далі, — саме тієї частини в списку немає.

Групові політики: хто створює, хто лінкує, хто редагує — і що лежить у SYSVOL

Що це і чому небезпечно

GPO — це механізм виконання коду на кожній машині домену за розкладом. Хто контролює політику на OU з серверами, контролює ці сервери: скрипт при вході, запланована задача, встановлення MSI, членство в локальній групі Administrators через Restricted Groups. Політика на OU Domain Controllers — те саме на контролерах домену: повністю штатний шлях у Tier 0.

Контроль складається з чотирьох незалежних частин, і жодної не видно там, куди дивляться найчастіше. Створювати GPO дозволяє Group Policy Creator Owners або делегування на контейнері CN=Policies: створений GPO ще ні до чого не привʼязаний. Лінкувати — писати в атрибут gPLink OU, домену або сайту; делегується окремо і в членстві груп не зʼявляється. Редагувати — ACL на обʼєкті groupPolicyContainer плюс дозволи NTFS на його папці в SYSVOL; половини мають збігатися, а розходження перевіряють рідко. Власник (creator owner) переписує ACL своєї політики попри те, що в ньому написано: звільнений власник десяти політик — типова знахідка.

Окремо — сайти: лінк GPO можна повісити на обʼєкт сайту в Configuration NC. Він діє на всі машини майданчика незалежно від OU, і в дереві доменних OU його не видно.

І вміст SYSVOL. Group Policy Preferences до виправлення MS14-025 дозволяли зберігати пароль у полі cpassword у XML-файлі політики; ключ шифрування Microsoft опублікувала сама. Оновлення закрило створення нових таких значень, але не видалило старі. Файли лежать у SYSVOL, який за замовчуванням читає кожен користувач домену.

Як подивитися вручну

  1. GPMC (gpmc.msc) → ліс → домен → вузол Group Policy Objects → вкладка Delegation: хто може створювати GPO.
  2. Для кожного GPO: Delegation → кнопка Advanced (вікно ACL) — Edit settings, delete, modify security і Full control у принципалів поза Domain Admins / Enterprise Admins / SYSTEM / Group Policy Creator Owners; там само вкладка Owner — власник.
  3. Для домену і кожної OU: Delegation → у списку Permission — Link GPOs (хто привʼязує політики до цього вузла), потім Perform Group Policy Modeling analyses і Read Group Policy Results data — виписати теж.
  4. Вкладка Group Policy Inheritance на кожній OU: що фактично діє з урахуванням успадкування, блокування і Enforced.
  5. OU Domain Controllers окремо: злінковані GPO, власники, ACL; звіт Settings на Default Domain Controllers Policy (див. перевірку 3, крок 7).
  6. Сайти: GPMC → вузол Sites; не видно — правою на Sites → Show Sites → відзначити потрібні. Вкладка Linked Group Policy Objects по кожному сайту. Порожньо — гарна відповідь.
  7. SYSVOL: \\corp.local\SYSVOL\corp.local\Policies у провіднику — звичайне читання мережевої шари. Дати змін папок показують, коли політики реально правили.
  8. Правою на GPO → Save Report: HTML локально, read-only, зручно для базової лінії. Пункт Edit не використовувати.

Команда

PowerShell
# Спершу — ПРЕАМБУЛА з розділу 0 ($dom, $dc).
$polDN  = "CN=Policies,CN=System,$($dom.DistinguishedName)"
$confNC = (Get-ADRootDSE -Server $dc).configurationNamingContext
$risky  = 'GenericAll','GenericWrite','WriteDacl','WriteOwner','WriteProperty','CreateChild','DeleteChild'

# 1. Усі GPO: імʼя, власник, шлях у SYSVOL, дата зміни
$gpos = Get-ADObject -Server $dc -SearchBase $polDN -LDAPFilter "(objectClass=groupPolicyContainer)" `
  -Properties displayName,gPCFileSysPath,whenChanged,whenCreated,versionNumber,nTSecurityDescriptor

$gpos | Select-Object displayName,whenChanged,versionNumber,
  @{n='Owner';e={ $_.nTSecurityDescriptor.Owner }},gPCFileSysPath

# 2. Хто має права на запис у конкретні GPO (поза очікуваними принципалами)
$gpoAcl = $gpos | ForEach-Object {
    $name = $_.displayName
    $_.nTSecurityDescriptor.Access |
      Where-Object { $_.AccessControlType -eq 'Allow' } |
      Where-Object { (($_.ActiveDirectoryRights -split ',\s*') | Where-Object { $_ -in $risky }).Count -gt 0 } |
      Select-Object @{n='GPO';e={$name}},
                    @{n='Principal';e={$_.IdentityReference.ToString()}},
                    ActiveDirectoryRights,IsInherited
}
$gpoAcl | Select-Object GPO,Principal,ActiveDirectoryRights,IsInherited

# 3. Де які GPO злінковано: домен + усі OU
Get-ADObject -Server $dc -Identity $dom.DistinguishedName -Properties gPLink,gPOptions |
  Select-Object DistinguishedName,gPLink,gPOptions
Get-ADOrganizationalUnit -Server $dc -Filter * -Properties gPLink,gPOptions |
  Where-Object { $_.gPLink } | Select-Object Name,gPLink,gPOptions,DistinguishedName

# 4. САЙТИ — лінки, яких не видно в дереві доменних OU
Get-ADObject -Server $dc -SearchBase "CN=Sites,$confNC" -LDAPFilter "(objectClass=site)" `
  -Properties gPLink,gPOptions | Where-Object { $_.gPLink } |
  Select-Object Name,gPLink,DistinguishedName

# 5. Хто може ЛІНКУВАТИ: право запису в атрибут gPLink.
#    schemaIDGUID gPLink    = f30e3bbe-9ff0-11d1-b603-0000f80367c1
#    schemaIDGUID gPOptions = f30e3bbf-9ff0-11d1-b603-0000f80367c1
$linkGuids = @('f30e3bbe-9ff0-11d1-b603-0000f80367c1','f30e3bbf-9ff0-11d1-b603-0000f80367c1')
$linkTargets  = @()
$linkTargets += Get-ADObject -Server $dc -Identity $dom.DistinguishedName -Properties nTSecurityDescriptor
$linkTargets += Get-ADOrganizationalUnit -Server $dc -Filter * -Properties nTSecurityDescriptor

$linkTargets | ForEach-Object {
    $dn = $_.DistinguishedName
    $_.nTSecurityDescriptor.Access |
      Where-Object { $_.AccessControlType -eq 'Allow' -and $_.ObjectType.ToString() -in $linkGuids } |
      Select-Object @{n='Container';e={$dn}},
                    @{n='Principal';e={$_.IdentityReference.ToString()}},
                    ActiveDirectoryRights,IsInherited
}

# 6. Права на сам контейнер Policies — хто може створювати нові GPO
dsacls $polDN

# 7. SYSVOL: залишки cpassword у Group Policy Preferences (читання файлів, без змін)
Get-ChildItem -Path "\\corp.local\SYSVOL\corp.local\Policies" -Recurse -Include *.xml -ErrorAction SilentlyContinue |
  Select-String -Pattern "cpassword" |
  Select-Object Path,LineNumber

# 8. SYSVOL: скрипти, у яких може лежати щось зайве (тільки перелік і пошук ключових слів)
Get-ChildItem -Path "\\corp.local\SYSVOL\corp.local\Policies" -Recurse -Include *.bat,*.cmd,*.ps1,*.vbs -ErrorAction SilentlyContinue |
  Select-String -Pattern "password","net use","runas" -SimpleMatch |
  Select-Object Path,LineNumber

# 9. Вивантаження (файли на вашій машині)
$gpoAcl | Export-Csv -NoTypeInformation -Encoding UTF8 -Path "$HOME\Desktop\gpo-acl.csv"

Що вона робить: у команді 1 versionNumber і whenChanged показують, які політики живі, а які не чіпали десять років. У команді 2 IsInherited = False промовисте: ACE на GPO майже завжди виставляють руками.

Команди 3 і 4 читають gPLink — рядок формату [LDAP://cn={GUID},cn=policies,cn=system,DC=...;0], де число після крапки з комою кодує стан лінка (0 — увімкнено, 1 — вимкнено, 2 — Enforced, 3 — вимкнено і Enforced). Команда 5: право лінкувати — це WriteProperty на атрибут gPLink, і ACE фільтруються за його schemaIDGUID — той самий Link GPOs, але по всьому домену.

Команди 7 і 8 читають SYSVOL по SMB: Select-String шукає рядок у файлах і нічого не змінює. Знахідка в команді 8 — привід прочитати рядок очима, а не автоматично проблема. Замініть corp.local на своє доменне імʼя.

Мінімальні права: звичайний доменний користувач: Authenticated Users за замовчуванням мають Read на GPO і на SYSVOL, а gPLink, nTSecurityDescriptor і Configuration NC читає будь-хто. Чесний виняток: якщо в GPO знято Read для Authenticated Users (буває при таргетуванні за групою безпеки), ви його не прочитаєте — це не помилка, зафіксуйте як «недоступний для перевірки» окремим рядком. Дозволи NTFS на папках SYSVOL читаються теж, але їх зіставлення з ACL обʼєкта в каталозі — ручна робота поза цією перевіркою.

Що НЕ змінює: нічого. Get-ADObject, Get-ADOrganizationalUnit, Get-ChildItem, Select-String — читання; dsacls без ключів модифікації — друк; Export-Csv — локальний файл.

У GPMC найнебезпечніші два пункти. Edit відкриває редактор GPME — не використовувати взагалі. Group Policy Modeling і Group Policy Results ініціюють збір даних, причому Results звертається до цільового хоста віддалено; без окремого погодження не запускати. Save Report безпечний. У вікні Advanced на вкладці Delegation — тільки Cancel.

Слід у логах:

  • 5145 (A network share object was checked) — від команд 7–8: рекурсивний обхід SYSVOL дасть відчутну кількість подій при ввімкненому Detailed File Share auditing. Другий за помітністю крок методички після перевірки 3.
  • 4663 (An attempt was made to access an object) — якщо на папках SYSVOL налаштовано SACL.
  • 4624/4634 Logon Type 3 на DC (LDAP) і на файловому доступі до SYSVOL.
  • 1644 — може зібрати читання дескрипторів по всіх OU у команді 5.
  • 4662 — на обʼєктах GPO, якщо SACL налаштовано.
  • Microsoft Defender for Identity — posture-оцінка Reversible passwords found in GPOs, та сама перевірка, що й команда 7, тільки регулярна.
  • Зустрічні дані для запиту в SOC: 5136 по атрибуту gPLink (хто і коли перелінковував політики) і 5137/5141 по створенню й видаленню обʼєктів у CN=Policies.

Що сказати SOC окремим рядком: «рекурсивне читання SYSVOL по SMB з хоста X у вікні Z, пошук за ключовими словами, змін не буде». Без цього попередження сплеск 5145 виглядає рівно так, як виглядає підготовка до атаки.

Як читати результат:

  1. Спершу — GPO на OU Domain Controllers: власник, принципали з правом запису, дата останньої зміни. Найкоротший і повністю штатний маршрут у Tier 0 в цій методичці.
  2. Вивід команди 5. Право лінкувати до OU з серверами плюс членство в Group Policy Creator Owners — повний шлях: створити GPO → прилінкувати → виконання коду.
  3. Лінки на сайтах (команда 4). Кожен непорожній рядок вимагає пояснення: спосіб доставки нетиповий і маловидимий.
  4. Власники GPO (команда 1). Звірте унікальних власників зі списком діючих співробітників; хто вже не в компанії — окремий рядок.
  5. cpassword (команда 7). Навіть одна знахідка — пароль, доступний кожному в домені. Виправлення поза цією методичкою, але дізнатися треба сьогодні.
  6. IsInherited = False у команді 2 — рішення конкретної людини; IsInherited = True — проблема лежить на OU і належить перевірці 5.
  7. versionNumber = 0 зі свіжим whenCreated — створений і ніколи не налаштований GPO: нешкідливо, але часто це слід чогось незавершеного.

Межа методу

Чого цим способом не побачите: ви побачили, хто керує механізмом, а не що він робить і до чого це призводить.

  • Вміст політик не проаналізовано. Ви прочитали ACL і лінки, а не налаштування: що роздає Restricted Groups, які скрипти запускаються при вході, які задачі створює політика — це звіт Settings по кожному GPO, і читати його доводиться очима.
  • Результуючої політики (RSoP) немає. Яка комбінація політик діє на конкретну машину з урахуванням успадкування, блокування, Enforced, WMI-фільтрів і фільтрації за групою безпеки — з gPLink не обчислюється, а інструмент для цього (Group Policy Results) звертається до цільового хоста, тобто виходить за межі «читання каталогу».
  • WMI-фільтри не враховані. Політика може бути злінкована й неактивна через фільтр — і навпаки.
  • Розходження між ACL обʼєкта і NTFS на папці SYSVOL не перевірено — відомий клас проблем, що виявляється лише ручним зіставленням двох різних систем дозволів.
  • Локальні політики на машинах узагалі поза каталогом.
  • Історії немає. Хто перелінкував політику минулого місяця — тільки в подіях 5136, якщо аудит модифікацій налаштовано.
  • Пріоритету немає. Сорок GPO з нестандартними ACL виглядають однаково; який із них злінковано туди, де це справді важливо, доводиться зіставляти руками з виводом команд 3–4.

Безкоштовні інструменти, що це автоматизують: RSAT — GPMC (gpmc.msc) з вкладками Delegation, Group Policy Inheritance і Settings, ADSI Edit, ldp.exe, in-box dsacls.exe. AD ACL Scanner — знімки ACL по контейнеру CN=Policies і порівняння між ними. PingCastle Community — правила щодо делегування GPO, GPO з паролями і політик на Domain Controllers (умови ліцензії — див. перевірку 1). Purple Knight — індикатори щодо GPP-паролів і небезпечних налаштувань політик. Microsoft Defender for Identity — Reversible passwords found in GPOs регулярно, але потребує E5-ліцензії й сенсорів. BloodHound CE — ребра GpLink: які машини потрапляють під контроль через конкретну політику.

Частина III. Хмара і стик

Entra ID: ролі, постійні призначення, застосунки і гості

Що це і чому небезпечно

У локальному AD привілей — це майже завжди членство в групі. У Entra ID це не так, і перенесена звичка дає сліпу зону.

Ролі призначаються трьома способами: постійно користувачу; постійно групі (role-assignable group); через PIM, як eligible — право активувати роль на кілька годин. Портал показує не все одразу, і різницю між Active і Eligible легко проґавити.

Ролей Tier 0 більше однієї. Крім Global Administrator: Privileged Role Administrator призначить собі будь-яку роль, включно з першою; Privileged Authentication Administrator змінить методи автентифікації будь-кого, включно з глобальним адміністратором, — тобто захопить його; Application Administrator і Cloud Application Administrator додадуть облікові дані до наявного застосунку і успадкують його права; Hybrid Identity Administrator керує стиком із локальним AD (перевірка 7); Directory Synchronization Accounts — службова роль облікового запису синхронізації, якої не має мати жодна людина.

Застосунки — друга площина привілеїв. Service principal з дозволом застосунку (app-only) RoleManagement.ReadWrite.Directory призначає ролі, AppRoleAssignment.ReadWrite.All — видає собі будь-які інші дозволи, Directory.ReadWrite.All — змінює обʼєкти каталогу. Це діє без користувача і без MFA — за секретом або сертифікатом у чиємусь коді; умовний доступ до app-only потоків не застосовується взагалі, і це конструкція, а не налаштування. Власник service principal може додати до нього обліковий запис і скористатися його дозволами, не маючи жодної ролі каталогу.

Гості. Обліковий запис із userType = Guest, якому призначено роль каталогу, керується чужим тенантом і чужою політикою автентифікації.

Як подивитися вручну

  1. Портал Entra (entra.microsoft.com) під роллю Global Reader або Security Reader.
  2. Roles & adminsAssignments кожної значущої ролі, з перемиканням фільтра Active / Eligible: це різні списки, виписати обидва.
  3. Ролі, які треба перевірити обовʼязково: Global Administrator, Privileged Role Administrator, Privileged Authentication Administrator, Authentication Administrator, User Administrator, Application Administrator, Cloud Application Administrator, Hybrid Identity Administrator, Intune Administrator, Exchange Administrator, Security Administrator, Directory Synchronization Accounts, Partner Tier2 Support.
  4. У кожному призначенні — колонка Scope (весь каталог чи адміністративна одиниця) і Type (Direct / Group).
  5. З ліцензією P2: Identity Governance → Privileged Identity Management → Microsoft Entra roles → Assignments — eligible/active, терміни й налаштування активації. Без P2 розділ порожній або недоступний — це теж відповідь.
  6. Enterprise applications → All applications → Permissions → вкладка Admin consent: шукати Application permissions (не Delegated) з іменами на .ReadWrite.All, RoleManagement.*, AppRoleAssignment.*, Directory.*.
  7. Там само → Owners.
  8. App registrations → Certificates & secrets: кількість і дати закінчення. Секрет без терміну або з терміном на роки — окремий рядок.
  9. Users → фільтр User type = Guest → перетнути з переліком із кроку 2.
  10. Protection → Conditional Access → у кожній політиці вкладка UsersExclude. Виписати всі винятки.
  11. Break-glass: аварійні облікові записи існують, виключені з CA свідомо й задокументовано. Немає — окремий рядок звіту.

Команда

PowerShell
# Одноразова автентифікація read-only. Потребує адміністративної згоди в тенанті (див. розділ 0).
Connect-MgGraph -NoWelcome -Scopes @(
  "Directory.Read.All","RoleManagement.Read.Directory",
  "Application.Read.All","Policy.Read.All","AuditLog.Read.All"
)

# 1. Усі призначення ролей каталогу з розгорнутими принципалом і роллю
$assign = Get-MgRoleManagementDirectoryRoleAssignment -All -ExpandProperty Principal,RoleDefinition
$assign | Select-Object `
  @{n='Role';e={ $_.RoleDefinition.DisplayName }},
  @{n='PrincipalType';e={ $_.Principal.AdditionalProperties['@odata.type'] }},
  @{n='Principal';e={ $_.Principal.AdditionalProperties['displayName'] }},
  @{n='UPN';e={ $_.Principal.AdditionalProperties['userPrincipalName'] }},
  DirectoryScopeId | Sort-Object Role

# 2. Зведення: скільки постійних призначень у кожній ролі
$assign | Group-Object { $_.RoleDefinition.DisplayName } |
  Select-Object Name,Count | Sort-Object Count -Descending

# 3. PIM: право активувати роль (eligible). Потребує ліцензії P2 — інакше порожньо.
Get-MgRoleManagementDirectoryRoleEligibilitySchedule -All -ExpandProperty Principal,RoleDefinition -ErrorAction SilentlyContinue |
  Select-Object @{n='Role';e={ $_.RoleDefinition.DisplayName }},
                @{n='Principal';e={ $_.Principal.AdditionalProperties['displayName'] }},
                @{n='Until';e={ $_.ScheduleInfo.Expiration.EndDateTime }}

# 4. Хто з носіїв ролей — гість або синхронізований з локального AD
$roleUserIds = $assign | Where-Object { $_.Principal.AdditionalProperties['@odata.type'] -like '*user' } |
                ForEach-Object { $_.PrincipalId } | Select-Object -Unique
$roleUserIds | ForEach-Object {
    Get-MgUser -UserId $_ -Property id,displayName,userPrincipalName,userType,onPremisesSyncEnabled,accountEnabled -ErrorAction SilentlyContinue
} | Select-Object DisplayName,UserPrincipalName,UserType,OnPremisesSyncEnabled,AccountEnabled

# 5. Дозволи ЗАСТОСУНКІВ (app-only) на Microsoft Graph — паралельна площина привілеїв
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$roles   = @{}; $graphSp.AppRoles | ForEach-Object { $roles[$_.Id.ToString()] = $_.Value }

Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $graphSp.Id -All |
  Select-Object @{n='App';e={ $_.PrincipalDisplayName }},
                @{n='Permission';e={ $roles[$_.AppRoleId.ToString()] }},
                CreatedDateTime |
  Sort-Object Permission

# 6. Облікові дані service principal: секрети й сертифікати, зокрема безстрокові
Get-MgServicePrincipal -All -Property id,displayName,appId,keyCredentials,passwordCredentials |
  Where-Object { $_.KeyCredentials.Count -or $_.PasswordCredentials.Count } |
  Select-Object DisplayName,AppId,
    @{n='Secrets';e={ $_.PasswordCredentials.Count }},
    @{n='Certs';e={ $_.KeyCredentials.Count }},
    @{n='EarliestExpiry';e={ (($_.PasswordCredentials.EndDateTime + $_.KeyCredentials.EndDateTime) | Sort-Object)[0] }}

# 7. Політики умовного доступу і їхні винятки
Get-MgIdentityConditionalAccessPolicy -All |
  Select-Object DisplayName,State,
    @{n='ExcludedUsers';e={ $_.Conditions.Users.ExcludeUsers -join ';' }},
    @{n='ExcludedGroups';e={ $_.Conditions.Users.ExcludeGroups -join ';' }},
    @{n='ExcludedRoles';e={ $_.Conditions.Users.ExcludeRoles -join ';' }}

# 8. Ліцензії тенанта — від них залежить, які з перевірок узагалі можливі
Get-MgSubscribedSku | Select-Object SkuPartNumber,ConsumedUnits,@{n='Enabled';e={$_.PrepaidUnits.Enabled}}

# 9. Вивантаження (файл на вашій машині)
$assign | Select-Object @{n='Role';e={$_.RoleDefinition.DisplayName}},
                        @{n='Principal';e={$_.Principal.AdditionalProperties['displayName']}} |
  Export-Csv -NoTypeInformation -Encoding UTF8 -Path "$HOME\Desktop\entra-roles.csv"

Що вона робить: Connect-MgGraph встановлює читацьку сесію — це автентифікація, а не операція над каталогом: усі scopes закінчуються на .Read.*.

Команда 1 бере Get-MgRoleManagementDirectoryRoleAssignment, а не Get-MgDirectoryRole: останній повертає лише активовані ролі, і роль із неактивним зараз PIM-призначенням там може не зʼявитися.

Команда 3 читає PIM-права (eligible). Порожньо — або таких призначень немає, або немає ліцензії P2, без якої PIM недоступний; розрізняє команда 8 (перелік SKU). Не читайте порожній вивід як «усе постійне» без цієї перевірки.

Команда 5 — головна і найменш очевидна: застосунки з дозволами типу «застосунок» на Microsoft Graph (00000003-0000-0000-c000-000000000000 — сталий AppId Graph у кожному тенанті).

Команда 6 показує, у кого з service principal є секрети й сертифікати і коли закінчується найближчий. Команда 7 витягує винятки з політик умовного доступу — у порталі їх довелося б клацати по одній.

Мінімальні права: Global Reader або Security Reader (обидві read-only), для журналів входів — Reports Reader. Ручний шлях працює рівно на цих ролях і не потребує жодної згоди. Командний — одноразової адміністративної згоди тенанта на read-only scopes для Microsoft Graph PowerShell: вона не робить вас адміністратором, а лише дозволяє читати те, що ваша роль і так бачить у порталі.

Що НЕ змінює: нічого. Усі командлети — Get-, scopes виключно читацькі, і API не прийме запис із таким токеном; під Global Reader кнопки збереження в порталі недоступні. Але не заходьте сюди під роллю з правом запису «щоб не перемикатися»: у Entra багато сторінок, де активна кнопка виглядає безневинно.

Слід у логах:

  • Журнал входів Entra (Sign-in logs) — перше підключення до Graph PowerShell дасть інтерактивний вхід від вашого імені і від застосунку Microsoft Graph PowerShell, подальші виклики — неінтерактивні або service principal sign-ins. Єдиний гарантований слід.
  • Журнал аудиту Entra фіксує зміни, а не читання: ваших Get- там не буде — скажіть це SOC заздалегідь.
  • Microsoft Graph activity logs фіксують читацькі виклики API — але лише за ліцензії Entra ID P1/P2 і ввімкненого diagnostic setting з експортом у Log Analytics або сховище. У більшості тенантів це не ввімкнено.
  • Microsoft Defender for Cloud Apps (якщо є) може позначити нетипову активність адміністративного читання.
  • Зустрічні дані — від SOC або самому під Reports Reader: аудит за категорією RoleManagement за останні 90 днів (хто кому призначав ролі) і ApplicationManagement (кому додавали облікові дані до застосунків). Друге — найцінніше і найрідше переглядуване.

Як читати результат:

  1. Порахуйте постійні призначення Global Administrator. Мікрософт рекомендує мінімум і роботу через PIM; число залежить від вас, але має бути назване вголос і мати список імен.
  2. Далі — «ролі, що ведуть до нього»: Privileged Role Administrator, Privileged Authentication Administrator, Application Administrator, Cloud Application Administrator. Їхні носії у більшості моделей загроз еквівалентні глобальним адміністраторам, а в звітах їх не рахують.
  3. PrincipalType = група: фактичний склад ролі — це склад групи, і в звіті ролей його не видно. Розкрийте окремо.
  4. OnPremisesSyncEnabled = True у носія ролі — точка гібридного важеля. Порахуйте такі рядки: це число F для аркуша екстраполяції розділу 13 і вхід у перевірку 7.
  5. UserType = Guest серед носіїв ролей — короткий список, який має бути порожнім або повністю поясненим.
  6. Вивід команди 5. Шукайте RoleManagement.ReadWrite.Directory, AppRoleAssignment.ReadWrite.All, Directory.ReadWrite.All, Application.ReadWrite.All, PrivilegedAccess.ReadWrite.AzureAD, Domain.ReadWrite.All. Кожен рядок — застосунок, еквівалентний адміністративній ролі, але поза MFA і умовним доступом; питання: хто власник, де лежить секрет, хто має доступ.
  7. Команда 6. Безстрокові або довгограючі секрети на застосунках із широкими дозволами — окремий рядок звіту.
  8. Команда 7. Кожен виняток із політики MFA зіставте зі списком носіїв ролей: адміністратор серед виключених — політика, якої для нього немає.

Межа методу

Чого цим способом не побачите: ви отримали список носіїв прав у тенанті; суміжні площини — поза кадром.

  • Azure RBAC — це інша площина. Ролі каталогу Entra і ролі підписок Azure (Owner, Contributor, User Access Administrator на підписці чи групі керування) — різні системи. Global Administrator дістає доступ до підписок через перемикач elevate access, і в ролях каталогу це не видно. Azure RBAC ця перевірка не покриває.
  • Що застосунок робить насправді. Ви бачите видані дозволи, а не використані: чи звертався service principal до Graph за останній рік — це журнали, а не каталог.
  • Де лежать секрети застосунків. У Key Vault, у змінних CI/CD, у чиємусь .env — каталог знає лише про їх існування й термін.
  • Ефективність умовного доступу. Ви бачите політики й винятки, а не результат обчислення для конкретного входу; What If рахує по одному сценарію за раз.
  • Делеговані дозволи (on-behalf-of) не покриті. Команда 5 читає лише application permissions; делеговані працюють інакше й потребують окремого зрізу Get-MgOauth2PermissionGrant.
  • Адміністративні одиниці (Administrative Units) ускладнюють картину: роль може діяти в межах AU, DirectoryScopeId це показує, але сенс кожної AU зʼясовується окремо.
  • Дрейф. Призначення через PIM тимчасові за визначенням: неактивне зараз може бути активним завтра. Знімок active-призначень системно занижує картину.
  • Стик із локальним AD тут лише позначено колонкою OnPremisesSyncEnabled — сам стик розбирає перевірка 7, і навіть вона дає два списки, а не одну модель.

Безкоштовні інструменти, що це автоматизують: Microsoft Entra ID Recommendations — щоденний аналіз конфігурації тенанта тими самими read-only ролями (розділ 14). Microsoft Secure Score / Identity Secure Score — оцінка з історією і порівнянням, уже ввімкнена. Microsoft Graph PowerShell відкритий і безкоштовний. Purple Knight покриває Entra ID і Okta поряд з AD — один звіт на дві сторони. Azure AD Assessment / Entra assessment (відкриті скрипти Microsoft) дають структурований звіт по тенанту. Чого немає — зведення ролей каталогу, дозволів застосунків і Azure RBAC в одну модель із маршрутами; це тема розділу 13.

Гібридний стик: Entra Connect, синхронізовані привілеї, Seamless SSO

Що це і чому небезпечно

Шість попередніх перевірок дали два окремі списки: локальний і хмарний. Ця перевірка — про те, що між ними: атака бачить це «між» одним обʼєктом, а звіти — ні.

Обліковий запис синхронізації в локальному AD. Entra Connect створює службовий обліковий запис (в Express-конфігурації — імʼя виду MSOL_<hex>). Для Password Hash Sync йому потрібні права реплікації DS-Replication-Get-Changes і DS-Replication-Get-Changes-All — те саме, що в перевірці 3 називалося DCSync. Це задокументована вимога: обліковий запис, здатний прочитати хеші всього домену, живе у звичайній OU, а його пароль зберігається на сервері Entra Connect у придатному до використання вигляді.

Сервер Entra Connect — це Tier 0, той самий рівень довіри, що й контролер домену: облікові дані синхронізаційного акаунта, конфігурація правил синхронізації і — при Pass-through Authentication — агент, через якого проходять перевірки паролів. Обслуговують його зазвичай ті самі команда й інструменти, що й рядові сервери.

Обліковий запис синхронізації в хмарі. У тенанті це принципал із роллю Directory Synchronization Accounts (імʼя виду Sync_<сервер>_<hash>) із правом запису в каталог. Жодна людина не має бути в цій ролі.

Seamless SSO і обʼєкт AZUREADSSOACC$. З безшовним єдиним входом у локальному AD зʼявляється компʼютерний обʼєкт AZUREADSSOACC$, чий ключ Kerberos перевіряє квитки до хмарної служби. Хто здобув ключ — виготовить квиток від імені будь-якого синхронізованого користувача. Microsoft рекомендує регулярну ротацію; на практиці PasswordLastSet часто дорівнює даті розгортання.

Федерація. Домен типу Federated делегує довіру до токенів AD FS (або сторонньому постачальнику): ключ підпису токенів і сервери AD FS стають обʼєктами рівня Tier 0, компрометація яких відкриває хмару в обхід усього іншого.

Синхронізовані привілейовані акаунти. Локальний обліковий запис, синхронізований у хмару і призначений на роль Entra, має дві незалежні поверхні керування: локальну (пароль скидає helpdesk із делегованим правом) і хмарну (MFA й умовний доступ). Атака вибирає слабшу.

msDS-KeyCredentialLink. Атрибут із ключами Windows Hello for Business: хто може писати в нього на чужому обʼєкті, той додасть власний ключ і автентифікується як цей обʼєкт, не змінюючи пароля і не лишаючи звичних слідів. Ми його читаємо: щоб побачити, де він заповнений.

Як подивитися вручну

  1. Локальний бік. ADUC (dsa.msc) → View → Advanced Features → правою на домені → Find → Custom Search → Advanced:
    (&(objectClass=user)(sAMAccountName=MSOL_*))
    Порожньо — синхронізація може йти під іншим обліковим записом: шукайте за описом або спитайте команду, що обслуговує Entra Connect.
  2. Відкрити його → Member Of (порожньо або мінімально) і Attribute EditorpwdLastSet, description.
  3. Звірити з перевіркою 3, команда 3: серед носіїв прав реплікації на корені домену він має бути єдиним поза контролерами домену, Domain Admins і Enterprise Admins.
  4. Знайти сервер Entra Connect (імʼя дасть команда інфраструктури): ADUC → Find → Computers. Його OU, злінковані GPO (перевірка 5) і права на цю OU (перевірка 3) — перевірка твердження «наш Connect-сервер захищений як Tier 0».
  5. Seamless SSO: ADUC → Find → Computers → AZUREADSSOACC → Properties → Attribute Editor → pwdLastSet; порахувати вік у роках.
  6. Хмарний бік. Портал Entra → Entra ConnectConnect Sync: метод автентифікації (Password Hash Sync / Pass-through Authentication / Federation), час останньої синхронізації, наявність staging-сервера.
  7. Портал → Roles & adminsDirectory Synchronization Accounts → Assignments: лише службові принципали.
  8. Портал → Custom domain names: тип кожного домену (Federated / Managed); федеровані виписати окремо.
  9. Портал → Users → кілька носіїв ролей із перевірки 6 → On-premises sync enabled.
  10. При Pass-through Authentication: портал → Entra Connect → Connect Sync → PTA agents; кожен сервер з агентом — теж Tier 0.

Команда

PowerShell
# ── ЛОКАЛЬНИЙ БІК (ПРЕАМБУЛА з розділу 0: $dom, $dc) ──────────────────────────

# 1. Обліковий запис(и) синхронізації в AD
Get-ADUser -Server $dc -LDAPFilter "(|(sAMAccountName=MSOL_*)(description=*Azure AD*)(description=*Entra*)(description=*AAD*))" `
  -Properties description,memberOf,pwdLastSet,whenCreated,userAccountControl |
  Select-Object sAMAccountName,description,
    @{n='PwdSet';e={ if ($_.pwdLastSet) { [datetime]::FromFileTime($_.pwdLastSet) } }},
    @{n='Groups';e={ $_.memberOf -join '; ' }},DistinguishedName

# 2. Носії прав реплікації на корені домену — звірка з перевіркою 3.
#    Тут має бути обліковий запис синхронізації і НІЧОГО зайвого.
$replGuids = @('1131f6aa-9c07-11d1-f79f-00c04fc2dcd2','1131f6ad-9c07-11d1-f79f-00c04fc2dcd2')
(Get-ADObject -Server $dc -Identity $dom.DistinguishedName -Properties nTSecurityDescriptor).nTSecurityDescriptor.Access |
  Where-Object { $_.ObjectType.ToString() -in $replGuids } |
  Select-Object @{n='Principal';e={$_.IdentityReference.ToString()}},ActiveDirectoryRights,IsInherited

# 3. Seamless SSO: вік ключа обʼєкта AZUREADSSOACC$
Get-ADComputer -Server $dc -LDAPFilter "(sAMAccountName=AZUREADSSOACC`$)" -Properties PasswordLastSet,whenCreated,DistinguishedName |
  Select-Object Name,whenCreated,PasswordLastSet,
    @{n='KeyAgeDays';e={ if ($_.PasswordLastSet) { [int]((Get-Date) - $_.PasswordLastSet).TotalDays } }}

# 4. msDS-KeyCredentialLink: де взагалі привʼязано ключі (Windows Hello / Key Trust)
Get-ADObject -Server $dc -SearchBase $dom.DistinguishedName -LDAPFilter "(msDS-KeyCredentialLink=*)" `
  -Properties objectClass,whenChanged |
  Group-Object objectClass | Select-Object Name,Count

# 5. Сервер Entra Connect у каталозі: де лежить і що на нього діє
Get-ADComputer -Server $dc -LDAPFilter "(name=SRV-AADC01)" -Properties operatingSystem,whenCreated,lastLogonTimestamp |
  Select-Object Name,operatingSystem,DistinguishedName

# ── ХМАРНИЙ БІК ───────────────────────────────────────────────────────────────
Connect-MgGraph -NoWelcome -Scopes @("Directory.Read.All","RoleManagement.Read.Directory","Domain.Read.All")

# 6. Стан синхронізації тенанта
Get-MgOrganization | Select-Object DisplayName,OnPremisesSyncEnabled,OnPremisesLastSyncDateTime

# 7. Тип автентифікації доменів: Managed чи Federated
Get-MgDomain -All | Select-Object Id,AuthenticationType,IsVerified,IsDefault,IsInitial

# 8. Роль Directory Synchronization Accounts — тут не має бути людей
$syncRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'Directory Synchronization Accounts'"
Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($syncRole.Id)'" -ExpandProperty Principal |
  Select-Object @{n='Type';e={ $_.Principal.AdditionalProperties['@odata.type'] }},
                @{n='Principal';e={ $_.Principal.AdditionalProperties['displayName'] }}

# 9. ТОЧКИ ГІБРИДНОГО ВАЖЕЛЯ: носії ролей Entra, синхронізовані з локального AD.
#    Це число F для аркуша екстраполяції в розділі 13.
$assign = Get-MgRoleManagementDirectoryRoleAssignment -All -ExpandProperty Principal,RoleDefinition
$hybrid = $assign |
  Where-Object { $_.Principal.AdditionalProperties['@odata.type'] -like '*user' } |
  ForEach-Object {
      $u = Get-MgUser -UserId $_.PrincipalId -Property id,displayName,userPrincipalName,onPremisesSyncEnabled,onPremisesSamAccountName -ErrorAction SilentlyContinue
      if ($u.OnPremisesSyncEnabled) {
          [pscustomobject]@{
              Role     = $_.RoleDefinition.DisplayName
              UPN      = $u.UserPrincipalName
              OnPremSam= $u.OnPremisesSamAccountName
          }
      }
  }
$hybrid | Sort-Object Role
"Точок гібридного важеля (F): " + @($hybrid).Count

# 10. Вивантаження (файл на вашій машині)
$hybrid | Export-Csv -NoTypeInformation -Encoding UTF8 -Path "$HOME\Desktop\hybrid-leverage.csv"

Що вона робить: команди 1–5 — локальний AD, 6–9 — тенант; сенс має їх зіставлення.

Команда 2 дублює запит із перевірки 3 як звірку: у здоровій конфігурації носіїв прав реплікації рівно стільки — контролери домену, Domain Admins, Enterprise Admins і один обліковий запис синхронізації.

У команді 3 важливе екранування ` $ ``: імʼя облікового запису закінчується знаком долара, і без зворотної лапки PowerShell підставить змінну. Це найпоширеніша причина, чому команда «нічого не знаходить» там, де Seamless SSO точно ввімкнено.

Команда 4 рахує обʼєкти з непорожнім msDS-KeyCredentialLink за класами, а не перелічує ключі: потрібна лише відповідь, чи живий цей механізм.

Команда 7 розрізняє Managed і Federated домени — один із небагатьох рядків методички, який змінює обсяг решти роботи.

Команда 9 — головна: носії ролей каталогу з перевірки 6, чий обліковий запис прийшов із локального AD. Це і є число F.

SRV-AADC01 у команді 5 замініть на імʼя вашого сервера синхронізації.

Мінімальні права: локальний бік — звичайний доменний користувач (description, pwdLastSet, PasswordLastSet, nTSecurityDescriptor, наявність msDS-KeyCredentialLink читаються за замовчуванням). Хмарний — Global Reader або Security Reader; портал працює на них без згоди, командний шлях потребує адміністративної згоди на read-only scopes (розділ 0).

Не вистачить доступу до самого сервера Entra Connect: його конфігурація читається тільки на ньому. Це інша площина, свідомо поза методичкою.

Що НЕ змінює: нічого. Локальні командлети — читання каталогу; хмарні — Get- з read-only scopes; Export-Csv — локальний файл.

Окремо і прямо: не ротуйте ключ AZUREADSSOACC$ усередині цього аудиту. Ротація — задокументована й потрібна процедура, але вона тимчасово ламає безшовний вхід для всіх користувачів, поки не пройде синхронізація. Її планують, узгоджують і роблять у вікні. Тут ви читаєте дату.

Так само не чіпайте нічого в майстрі Entra Connect на сервері: «просто подивитися» в ньому закінчується кроком, який пропонує застосувати конфігурацію.

Слід у логах:

  • Локально: 4624/4634 Logon Type 3 на цільовому DC; 4662, якщо на обліковому записі синхронізації або на корені домену налаштовано SACL — на корені частіше, ніж деінде.
  • Читання ACL кореня домену (команда 2) згадайте в листі до SOC окремим рядком, як і в перевірці 5.
  • У хмарі: підключення до Graph — у журналі входів. Журнал аудиту Entra фіксує зміни, а не читання, тож ваших Get- там не буде. Microsoft Graph activity logs зафіксували б читацькі виклики, але лише за P1/P2 і ввімкненого diagnostic setting.
  • Зустрічні дані, цінніші за ваш слід:

    • у журналі входів Entra — входи облікового запису синхронізації з IP, відмінних від сервера Connect;
    • у журналі аудиту Entra, категорія ApplicationManagement — додавання облікових даних до застосунків (перевірка 6);
    • у локальному SIEM — 4662 з правами реплікації від принципала, який не є контролером домену: найкоротший наявний детект DCSync, і його варто мати незалежно від цієї методички.

Як читати результат:

  1. Команда 2 — перший рядок підсумку. Якщо носіїв прав реплікації більше, ніж очікувано, решта розділу вторинна.
  2. Groups в команді 1. Обліковий запис синхронізації в Domain Admins — не «надійніша конфігурація», а зайвий привілей: потрібні йому права реплікації, а не членство. Часта знахідка після міграцій.
  3. KeyAgeDays у команді 3. Збіг із датою whenCreated означає, що ключ не ротували від дня розгортання. Не аварія, але рядок у плані.
  4. Команда 7, тип Federated. Кожен федерований домен робить ваш Tier 0 більшим, ніж описано тут: додаються сервери AD FS і сертифікат підпису токенів.
  5. Команда 8. Людина в ролі Directory Synchronization Accounts — рядок, який має бути порожнім.
  6. Команда 9 — головний результат. Для кожного рядка одне питання: яка з двох поверхонь слабша? Якщо хмарний бік захищено MFA і PIM, а локальний пароль того самого акаунта може скинути будь-хто з делегованим правом на його OU (перевірка 3), фактичний рівень захисту дорівнює локальному.
  7. Запишіть число F — воно знадобиться в аркуші екстраполяції розділу 13.
  8. Команда 4. Ненульова кількість обʼєктів із msDS-KeyCredentialLink робить питання «хто має право писати в цей атрибут на чужих обʼєктах» практичним; відповідь — у виводі перевірок 4 і 5.

Межа методу

Чого цим способом не побачите: ви позначили точки стику. Самого стику як єдиної моделі у вас немає.

  • Сервер Entra Connect залишився чорною скринькою. Версія, режим (staging чи ні), правила синхронізації, які OU потрапляють у скоуп, локальні адміністратори, наявність EDR — усе це читається на самому сервері, а не в каталозі.
  • Метод автентифікації в деталях. Ви бачите Federated чи Managed, але не бачите, чи ввімкнено PHS як запасний варіант при PTA, скільки агентів PTA живі й де вони стоять.
  • AD FS повністю поза кадром. Сертифікат підпису токенів, його термін, доступ до серверів ферми, чи не було експорту ключа — окрема інфраструктура з власним аудитом.
  • Зворотний напрямок (writeback). Group writeback, device writeback і password writeback дають хмарі право писати в локальний AD; наявність і обсяг цих можливостей з каталогу не читаються.
  • Правила синхронізації. Які атрибути й обʼєкти синхронізуються, чи є фільтрація за OU, чи не потрапляють у хмару локальні привілейовані акаунти — конфігурація Connect, а не каталог.
  • Ланцюг через стик. Ви бачите дві поверхні облікового запису, але не повний маршрут: «локальне делегування → скидання пароля → вхід у хмару → роль Entra → застосунок із широкими дозволами → назад у локальний AD через writeback». Кожна ланка є в різних розділах методички; маршруту немає в жодному.
  • Дрейф швидший, ніж деінде. Гібридна конфігурація змінюється з кожним проєктом міграції; знімок старіє швидше за локальний.

Безкоштовні інструменти, що це автоматизують: Entra Connect Health (потребує P1) показує стан синхронізації, помилки й агентів. Microsoft Entra ID Recommendations і Secure Score покривають частину гібридної гігієни. Purple Knight сканує AD і Entra ID одним проходом і має індикатори саме навколо стику — з безкоштовних це найближче до потрібного. PingCastle має окремий модуль для хмарної частини (умови ліцензії — перевірка 1). Forest Druid відповідає на суміжне «що належить до нашого Tier 0»: сервер Entra Connect — класичний приклад того, що в цей периметр потрапляє, а в документації не значиться. Немає в безкоштовному наборі єдиної моделі, де локальний обʼєкт і його хмарна проєкція — один вузол із двома поверхнями, а не два рядки у двох звітах.

Що ще варто перевірити

Пʼять тем нижче ми свідомо не розгорнули в покрокові перевірки. Причина щоразу одна з двох: або типова відповідь — «усе гаразд», або знахідка така, якої ви й так очікуєте. Це не робить їх непотрібними — просто це не те, на що варто витрачати перші півтори години.

  • Недооцінені групи. Account Operators, Backup Operators, Server Operators, Print Operators, DnsAdmins, Group Policy Creator Owners. За замовчуванням усі шість порожні; кожен непорожній рядок потребує імені, дати і причини. Backup і Server Operators — це Tier 0 без помʼякшень, просто без слова «admin» у назві.
  • Забуті привілеї. Фільтр (&(objectCategory=person)(objectClass=user)(adminCount=1)) дає обʼєкти, які колись були в захищених групах: успадкування в них вимкнене, ACL застиг на момент виходу. krbtgt у результаті — норма, а не знахідка. Це слід минулого, а не поточні права; поточні права дає перевірка 3.
  • Поверхня Kerberoasting. Фільтр (&(objectCategory=person)(objectClass=user)(servicePrincipalName=*)) плюс запит до SOC на події 4769 з типом шифрування 0x17. Наявність сервісних акаунтів із SPN — це механізм Kerberos, а не проблема; значення має їх перетин із Tier 0, і його ви вже бачили в перевірці 1.
  • AdminSDHolder і dSHeuristics. Прочитайте дескриптор безпеки CN=AdminSDHolder,CN=System і рядок dSHeuristics у конфігурації лісу, збережіть у файл. Найімовірніше, там усе штатно — і саме тому це базова лінія: цінність зʼявиться не сьогодні, а при порівнянні через квартал.
  • Гігієна облікових записів. Прапорці userAccountControl, вік ключа krbtgt, покриття LAPS, сплячі акаунти, ms-DS-MachineAccountQuota. Єдиний блок цього переліку, який PingCastle Community і Purple Knight закривають повністю і швидше за вас, — почніть із них, див. розділ 14.

Жодна з цих пʼяти тем не змінює висновку наступного розділу: усі вони дають ще списки. У зошиті лишилися ті сім, на яких найкраще видно, чого списку бракує.

Розділ 13. Що ви щойно зробили — і чого не змогли

Підсумок

У вас на руках сім списків. Жодного стану ви не змінили, жодних підвищених прав не використали, все зроблено з-під звичайного доменного користувача і читацької ролі в тенанті. Це більше, ніж має про свою поверхню ідентичностей середня організація.

І це все ще списки.

Чому список ≠ ланцюг

Кожен елемент нижче — легітимний. Кожен пройде ревʼю окремо. Кожен має власника, який пояснить, навіщо це так.

  1. Група Helpdesk-L2 має делеговане право Reset Password на OU Users. Легітимно — це буквально їхня посадова інструкція.
  2. У цій OU, серед решти обʼєктів, лежить сервісний акаунт svc-backup. Легітимно — його створили сім років тому, коли структура OU була іншою, і ніхто не мав причин його переносити.
  3. svc-backup входить до локальної групи Administrators на сервері резервного копіювання. Легітимно — інакше агент бекапу не працює.
  4. На тому ж сервері періодично працює задача під акаунтом із Domain Admins — або туди інтерактивно заходить адміністратор, коли щось падає. Легітимно — так усі й роблять.

Тепер прочитайте це знизу вгору. Будь-хто в Helpdesk-L2 може скинути пароль svc-backup, зайти під ним на сервер резервного копіювання з правами локального адміністратора і дістати з памʼяті облікові дані сесії Domain Admin. Чотири захищені рішення складаються в маршрут від рівня «перша лінія підтримки» до Tier 0.

Жоден із семи списків цього маршруту не показує. Список №3 (делегування на OU) покаже вам крок 1. Список №1 (Tier 0) покаже вам крок 4. Ребра між ними — не показує ніщо, бо ребра не є атрибутом обʼєкта. Вони існують тільки як відношення між обʼєктами, а відношення треба будувати, а не читати.

Чотири межі ручного підходу

1. Транзитивність ACL. dsacls і Get-Acl показують прямі права на конкретний обʼєкт. Вони не показують, що принципал X має WriteDacl на групу A, група A вкладена в групу B, а B має GenericAll на OU з Tier 0-сервісниками. Кожен крок читається окремо; композиція — ні. Обхід цього вручну має комбінаторну складність: щоб перевірити шляхи довжини 3, треба обійти кожне ребро від кожного кінця кожного ребра. Скільки це у вашому випадку — порахуйте нижче.

2. Пріоритезація і choke points. Припустімо, у вас 400 обʼєктів з нестандартними правами. Який виправляти першим? Ручний підхід відповіді не має, бо відповідь залежить не від властивостей обʼєкта, а від його позиції в графі: одне делегування, через яке проходить сорок маршрутів, важливіше за тридцять девʼять окремих. Знайти таку точку можна лише на побудованому графі — на списку її не видно принципово.

3. Дрейф у часі. Ваші сім списків — знімок конкретної години конкретного дня. Завтра створять сервісний акаунт, післязавтра проєкт попросить тимчасові права «на два тижні», через місяць звільниться людина, чий акаунт залишиться в трьох групах. Журнал аудиту Entra фіксує зміни в хмарі; на боці AD зміни ACL фіксуються лише за налаштованого SACL, а такого зазвичай немає. Знімок починає розходитися з реальністю з першої ж хвилини після зняття, і ви не знаєте, наскільки далеко розійшовся.

4. Гібридний стик AD + Entra в одній моделі. Розділи про локальне AD і розділи про тенант дають два списки — і між ними немає спільної системи координат. Локальний акаунт, синхронізований у хмару і призначений на роль Entra, у першому списку є одним рядком, у другому — іншим, а те, що це той самий важіль, тільки з двома кінцями, доводиться тримати в голові. Аналогічно у зворотний бік: акаунт Entra Connect, права на нього в локальному AD і його можливості в тенанті — три різні місця в трьох різних консолях. Атака цим стиком користується як одним обʼєктом. Ваші списки — ні.

Аркуш екстраполяції

Мета — не точна цифра, а порядок величини. Візьміть числа зі своїх списків.

Підставте свої числа. Порядок величини важливіший за точність.

Кроків «хто → на що має право»B × C
Можливих ланцюгів із 2 кроківB × C²
Можливих ланцюгів із 3 кроківB × C³
Годин, щоб перевірити 2-крокові вручну(B × C²) × 2 / 60
Точок гібридного важеляF
Днів між змінами90 / G

Рахується локально у вашому браузері. Жодне число не залишає цю сторінку — див. «Не надсилайте нам результати» в розділі 0.

Спершу два слова, якими рахуємо.

Крок — одна пара «хто → на що має право». У прикладі вище крок перший це Helpdesk-L2 → може скинути пароль svc-backup.

Ланцюг — кілька кроків поспіль, де кінець одного стає початком наступного. Той самий приклад — ланцюг із трьох кроків: Helpdesk-L2 → svc-backup → бекап-сервер → Domain Admin. Саме ланцюг доводить людину з першої лінії підтримки до Tier 0, і саме його не показує жоден із семи списків.

Рахуємо:

  1. Скільки всього кроків у вашому каталозіB × C. Кожен із B принципалів має права в середньому на C обʼєктів. Це та кількість, яку ви теоретично могли б переглянути очима за день-два.
  2. Скільки двокрокових ланцюгів теоретично можливоB × C². Звідки квадрат: у вас B × C кроків, і від кінця кожного можна піти далі — а там знову в середньому C варіантів. Три кроки — B × C³, чотири — B × C⁴. Це верхня межа, а не факт. Реальних маршрутів менше: не кожен крок стикується з наступним. Але наскільки менше — ви не знаєте, і в цьому вся проблема: щоб дізнатися, треба обійти всі.
  3. Скільки часу піде перевірити двокрокові ланцюги вручну — з розрахунку 2 хвилини на один: (B × C²) × 2 / 60 годин. Підставте свої числа. У більшості середовищ результат вимірюється не годинами.
  4. Точок гібридного важеля = F. Кожна — обʼєкт, який має дві незалежні поверхні керування, описані в двох різних розділах цієї методички.
  5. Термін придатності знімка: у середньому одна зміна кожні 90 / G днів. Через скільки днів ваш сьогоднішній набір списків розійдеться з реальністю настільки, що рішення на його основі буде помилковим — оцініть самі.

Якщо G виявилося невідомим, бо журналу змін ACL немає, — запишіть це окремим рядком. Це найдорожче з усього, що ви сьогодні дізналися.

Розділ 14. Безкоштовні інструменти: що вони закривають, а що ні

Огляд без знецінення. Усі перелічені інструменти корисні, деякі — дуже. Мета розділу — щоб ви не витратили квартал на очікування від інструмента того, чого він за конструкцією не робить.

Microsoft Secure Score / Identity Secure Score

Що дає. Наскрізна оцінка конфігурації тенанта проти набору практик Microsoft, з балами, історією і порівнянням із подібними організаціями. Identity Secure Score — ідентичнісна частина, зараз інтегрована в Entra recommendations. Безкоштовно, вже увімкнено, читається ролями Reports Reader / Security Reader / Global Reader.

Чого не дає. Це оцінка конфігурації, а не топології. Secure Score не знає, що у вас Tier 0, не бачить локальних ACL, не будує шляхів. Бали нормалізовані під усіх, тож «виправте це, воно дає +7» не означає «це у вашій інфраструктурі найнебезпечніше». Рекомендація виконана — бал зростає, навіть якщо реальний маршрут до вашого Tier 0 проходив зовсім поруч.

Microsoft Entra ID Recommendations

Що дає. Щоденний аналіз конфігурації тенанта, персоналізовані рекомендації з покроковим планом і переліком уражених ресурсів. Читається тими ж read-only ролями. У переліку зʼявилися й помітно «адешні» пункти в preview: Remove non-admin accounts with DCsync permissions, Remove access rights on suspicious accounts with the Admin SDHolder permission, Modify unsecure Kerberos delegations to prevent impersonation, Reduce lateral movement path risk to sensitive entities, Reversible passwords found in GPOs, Protect and manage local admin passwords with Microsoft LAPS.

Чого не дає. Частина рекомендацій потребує ліцензії P2, а «адешні» preview-пункти живляться даними Defender for Identity — без розгорнутих сенсорів вони у вас просто не зʼявляться, і відсутність рекомендації не означає відсутність проблеми. Формат залишається списком дій, а не графом: рекомендації не сортуються за тим, скільки маршрутів через кожну проходить.

Microsoft Defender for Identity

Що дає. Найсильніше з безкоштовно-в-складі-ліцензії: детекти в реальному часі (розвідка, Kerberoasting, DCSync, аномальні входи), identity posture assessments, інтеграція в Defender XDR.

Чого не дає — і що варто знати про ціну входу. Потрібна ліцензія рівня Microsoft 365 E5 / EMS E5 / окремий SKU і розгортання сенсорів на контролерах домену, AD FS, AD CS, Entra Connect — це проєкт, а не кнопка. Покриття закінчується там, де немає сенсора.

Окремо: у травні 2025 Microsoft вимкнула віддалений збір членства локальних груп адміністраторів через SAM-R. Ці дані живили карту Lateral Movement Paths — після зміни карта більше не оновлюється. Крок 3 із прикладу в розділі 13 (svc-backup у локальних адміністраторах бекап-сервера) саме звідти. Це не докір продукту — це факт, який треба враховувати, плануючи, звідки ви цю інформацію братимете тепер.

Microsoft Security Exposure Management

Що дає. Attack path analysis, critical asset management, наскрізні exposure insights — тобто саме та графова логіка, якої бракує Secure Score. Показує маршрути від точки входу до критичних активів.

Чого не дає. Ліцензійний поріг рівня E5: Microsoft 365 E5/A5, або E3 з надбудовою E5 Security, або EMS E5/A5, або Windows/Office E5. Якість результату прямо залежить від того, скільки джерел Defender у вас підключено — без MDI і MDE граф буде порожній там, де у вас найцікавіше. І це модель усередині екосистеми Microsoft: те, що поза нею, у графі не зʼявиться.

PingCastle

Що дає. Найшвидший спосіб отримати структуровану оцінку здоровʼя AD: score за чотирма категоріями, набір правил ризику з поясненнями, карта довіри між доменами, звіт у HTML, який реально показують керівництву. Запуск — хвилини, не дні.

Ліцензійна пастка, на якій регулярно спотикаються. Код розповсюджується під пропрієтарною ліцензією та Non-Profit OSL 3.0. Безкоштовно запускати бінарник можна доти, доки ви не отримуєте з цього дохід: комерційна компанія має право аудитувати власне середовище. Але якщо ви — інтегратор, MSP, пентестер чи аудитор і збираєтеся запускати PingCastle у чужих організаціях за гроші — потрібна платна комерційна ліцензія. Це не сіра зона, це прямо в ліцензії. Перевірте свій сценарій до першого запуску в клієнта, а не після.

Чого не дає. Це health check і набір правил, а не граф шляхів до обраної цілі. Питання «покажи всі маршрути до цієї конкретної групи» PingCastle не відповідає. Хмарна частина — окремий інструмент. Результат — знімок.

Purple Knight

Що дає. Безкоштовна оцінка з великою бібліотекою індикаторів експозиції та компрометації (у свіжих версіях — понад 200; число залежить від версії) по AD, Entra ID та Okta. Звіт із категоріями, балами й поясненнями «чому це важливо». Не потребує агентів, добре читається людиною, яка не є AD-архітектором.

Чого не дає. Це теж список індикаторів, а не граф. Пріоритезація — за важкістю індикатора взагалі, не за його позицією у вашій топології. Це знімок: щоб бачити дрейф, треба запускати регулярно й самому порівнювати звіти.

BloodHound Community Edition

Що дає. Єдиний у цьому переліку безкоштовний інструмент, який дає саме те, чого бракує розділу 13: справжній граф з транзитивними ребрами і запитами «покажи найкоротший шлях звідси до Domain Admins». Відкритий код, Cypher-запити, велика спільнота.

Чого не дає і чим коштує. Колектор SharpHound детектується Microsoft Defender Antivirus як HackTool і ловиться більшістю EDR — це інструмент червоних команд, і поводиться він відповідно. Практично це означає: винятки в EDR, узгодження з SOC заздалегідь, вікно для збору. Сам збір шумний: масові LDAP-запити плюс звернення до кінцевих точок. Збір членства локальних груп упирається в ті самі проблеми, що й у MDI. Результат — знімок бази, тож питання дрейфу залишається. Аналітика на кшталт систематичного пошуку choke points і керування шляхами як процесом — у комерційній редакції, не в CE. І, головне, це інструмент, який треба вміти читати: граф без методології дає красиву картинку і нуль рішень.

Forest Druid

Що дає. Розумний зсув перспективи: замість «звідки куди можна дійти взагалі» — «хто ззовні дотягується всередину периметра Tier 0». Підхід inside-out різко скорочує обсяг того, що треба розглядати, і добре відповідає на питання «що взагалі належить до нашого Tier 0, включно з тим, про що ми не думали». Безкоштовний.

Чого не дає. Це інструмент для однієї конкретної задачі — окреслити привілейований периметр. Не детекти, не безперервний моніторинг, не повна гібридна модель, не пріоритезація всього іншого, що лежить поза Tier 0.

І одна річ, якої більше немає

Microsoft Entra Permissions Management (CIEM) ретайрнуто. Продаж припинено з 1 квітня 2025, підтримка й сам сервіс закінчилися 1 листопада 2025 з автоматичним відключенням клієнтів. Якщо у вашій внутрішній документації або в чиємусь плані на рік він ще фігурує як компонент — план треба оновити. Частина сценаріїв перейшла під Defender CSPM, частина закривається сторонніми CIEM.

Загальний висновок розділу

Безкоштовний інструментарій закриває три різні задачі трьома різними інструментами: конфігураційна гігієна (Secure Score, Entra recommendations), набір відомих слабких місць (PingCastle, Purple Knight), граф і шляхи (BloodHound CE, Forest Druid, а з ліцензією E5 — Exposure Management). Кожен добрий у своєму. Проблема не в якості жодного з них, а в тому, що зшивати три моделі в одну картину, тримати її актуальною і кожні кілька тижнів повторювати збір — це роль людини, а не інструмента. Скільки це годин на місяць — ви щойно порахували в аркуші екстраполяції.

Джерела до розділу 14:

Розділ 15. Що далі

Якщо ви дійшли сюди з сімома заповненими списками і порахованим аркушем екстраполяції — ви зробили більше, ніж більшість, і тепер маєте власні числа замість чужих оцінок. Наступне питання очевидне: що робити з розривом між списком і ланцюгом, який ці числа щойно показали.

27 серпня о 15:00 ми проводимо вебінар, де розбираємо саме цей розрив: як з тих самих даних будується граф ідентичностей, чому choke point важливіший за окремий неправильний ACE, і як виглядає та сама інфраструктура, коли AD і Entra зведені в одну модель, а не в дві консолі. У другій половині показуємо Forestall — agentless-платформу, яка це робить без сенсорів на контролерах домену і без колектора, що ловиться EDR.

Приходьте зі своїми числами з аркуша екстраполяції: розмова про конкретні B × C² завжди змістовніша за розмову про індустрію загалом. Якщо вебінар не підходить за часом — напишіть, домовимося окремо; результати ваших перевірок при цьому все одно не надсилайте, вистачить порядків величини. І якщо після всього ви вирішите, що вам достатньо комбінації з розділу 14 — це теж нормальний і чесний результат цієї методички.

Забрати з собою

Методичка нікуди не дінеться — вона відкрита і без реєстрації. Але якщо зручніше мати її під рукою:

Надіслати собі на пошту Поділитися в LinkedIn (відкриється в новій вкладці)

Друк уже налаштований: навігація, кнопки й заклики не потрапляють на папір, фон світлішає, а посилання розкриваються повними адресами. Кожна перевірка починається з нової сторінки.