В чем соль: у нас уже есть достаточно известные утилиты, чтобы «отслеживать» изменения в Active Directory. Почему в кавычках? Потому что все подобные утилиты (ну, практически) используют один и тот же механизм под капотом: Get-ADObject -Filter * (выкачиваем информацию обо всех объектах и их свойствах) и далее просто парсим эти результаты на своем клиенте и ищем изменения.

Но при таком подходе есть некоторые нюансы:

  • Если вы когда-нибудь использовали подобную команду в отношении большого домена, то вы знаете, что выполнение этой команды может занимать очень большое время и неплохо так нагружает сеть. А если домен разрастается еще больше (20, 30, 50 тысяч объектов), то от этого плана вообще можно отказаться. Выполнение команды будет длиться бесконечно, время вывода информации растягивается (если кто собирал BloodHound с больших доменов, то понимает, о чем я). Можно, конечно, поставить задержку между запросами, но тогда возможность пропустить некоторые изменения увеличивается.

  • Но возможность пропустить события и так присутствует: если в рамках одного запроса было несколько изменений одного свойства, то вы не только пропустите значение свойства, но и вообще можете не узнать об изменениях. Например, можно пропустить включение/выключение какой-нибудь УЗ (true/false) (а если это админ какой-нибудь?)

  • Есть свойства, которые под собой содержат другие объекты, а в значении свойства содержится просто их название. Например, nTSecurityDescriptor. И здесь без раскрытия и проработки каждого свойства мы рискуем пропустить изменения. А это дополнительное время и ресурсы.

Использование сети на DC при запуске LDAPMonitor c хоста DESKTOP-5PHFK5J
Использование сети на DC при запуске LDAPMonitor c хоста DESKTOP-5PHFK5J
Вывод утилиты LDAPMonitor. Хоть и было изменение в ACL OU, но тут мы этого не увидим.
Вывод утилиты LDAPMonitor. Хоть и было изменение в ACL OU, но тут мы этого не увидим.

А хотелось бы подобную деятельность проводить эффективнее. И здесь нам может помочь знание о том, как работает репликация между контроллерами домена. Всё потому, что Active Directory - это, на мой взгляд, очень ресурсоэффективное создание, ибо создавалось оно во времена отсутствия быстрых сетей и безграничных ресурсов.

Вместо того, чтобы вытаскивать и сравнивать все объеты домена, мы можем следить за одним параметром контроллера домена и точечно запрашивать измененные свойства конкретных объектов.

Если у нас уже есть доменная УЗ и модуль ActiveDirectory для Powershell, то можно начинать. Дисклеймер: все команды из скринов ниже исполняются на недоменной машине через запуск командной строки от доменного пользователя (через runas). Так как комп недоменный, указываем адрес DC, к которому будет отправлять запросы.

Запрашиваем список контроллеров домена и выбираем из них любой (ну, желательно из того же сайта, если вдруг инфра огромная). Запрашиваем все свойства конкретного DC и смотрим на InvocationID.Guid. По сути, это уникальный идентификатор конкретного DC, по которому его можно идентифицировать.

Запускаем консоль от обычной доменной УЗ и проверяем доступ к DC
Запускаем консоль от обычной доменной УЗ и проверяем доступ к DC
(Get-ADDomainController WinAD -Server winad.trytopwn.me).InvocationID.Guid
Информация о контроллере winad.trytopwn.me
Информация о контроллере winad.trytopwn.me

Далее с помощью Get-ADReplicationUpToDatenessVectorTable и некоторых уточнений получаем USN из свойства USNFilter конкретного DC.

Get-ADReplicationUpToDatenessVectorTable winad.trytopwn.me -EnumerationServer 192.168.254.130 | where-object {$_.PartnerInvocationId.Guid -eq '5573797e-96f0-45e5-83f2-bea9ba4aa231'}
Обратите внимение на значение UsnFilter
Обратите внимение на значение UsnFilter

А что такое USN? Update Sequence Number (USN) — это счетчик изменений, которые произошли на конкретном контроллере домена. Каждый контроллер в Active Directory не только содержит (как минимум, должен) актуальную базу всех доменных объектов, но и может вносить туда изменения. По этой причине Microsoft должны были придумать надежный механизм, который будет поддерживать доменную базу в актуальном состоянии на всех DC. Для этой цели существует механизм репликации, который базируется не на времени внесения изменений, а на специальном счетчике USN.

Что стоит запомнить о нем:

  • Счетчик появляется при повышении уровня сервера до контроллера домена. Т.е. когда создается контроллер домена, то появляется и USN. А так как контроллеры создаются на протяжении всей жизни домена, это значит, что

  • У каждого контроллера домена USN является уникальным (не говорите потом, что не видели).

  • Изменение любого свойства любого объекта домена увеличивает USN (неважно, на каком DC в домене это изменение произошло). Как правило, каждое изменение увеличивает USN каждого DC на 1.

  • Каждый DC в домене знает локальные значения USN остальных DC. Это необходимо для более эффективной репликации изменений между контроллерами и помогает избежать передачи уже известной информации. За это отвечают две таблицы High-watermark vector (HWMV) и Up-to-dateness vector (UTDV) (присутствуют на каждом DC). Кстати, из второй мы и запрашиваем значение UsnFilter.

Вот за этим свойством мы и можем следить, чтобы определить, были ли какие-то изменения. Предположим, что USN увеличился. Что дальше? Давайте посмотрим на все свойства какого-нибудь объекта. Среди всего прочего, мы увидим вот такие:

  • uSNCreated — самое первое значение USN, которое относится к данному объекту. т. е., если вы возьмете все USN всех свойств нашего пользователя (на момент создания объекта) и выберете из них самое маленькое, то оно и будет здесь отображено.

  • uSNchanged — самое последнее значение USN, которое относится к данному объекту. Т.е., если вы возьмете все USN всех свойств нашего пользователя (в текущий момент времени) и выберете из них самое большое, то оно и будет здесь отображено.

Get-ADUser o.petrov -server winad.trytopwn.me -Properties uSNChanged,uSNCreated
Get-ADObject -LDAPFilter "(&(objectClass=*)(usnchanged>=225651))" -Server winad.trytopwn.me -IncludeDeletedObjects -Properties uSNChanged
Свойства uSNChanged и uSNCreated у объекта пользователя
Свойства uSNChanged и uSNCreated у объекта пользователя
Объекты, в которых были внесены изменения
Объекты, в которых были внесены изменения

Вот и следующий элемент для нашего пазла. Если у нас был USN, скажем, 2000. А потом он изменился на 2002, то мы можем просто запросить все объекты, значение USNChanged которых больше 2000 (в нашем примере значение предыдущего USN 225651). Таким образом получим объекты, которые были изменены. Но здесь мы видим только сам объект, а какие же свойства у него поменялись?

На этот вопрос ответит cmdlet Get-ADReplicationAttributeMetadata. В выводе у конкретного свойства нам интересны следующие атрибуты:

  • LastOriginatingChangeTime — время первичного изменения на исходном контроллере домена.

  • LastOriginatingChangeUsn — USN первичного изменения на исходном контроллере домена.

  • LocalChangeUsn — USN изменения контроллера домена, с которого мы получаем информацию. Если DC является исходным для этого изменения, то поле совпадет с LastOriginatingChangeUsn.

  • LastOriginatingChangeDirectoryServerIdentity — исходный контроллер домена (так как это информация о репликации, то и DistinguishedName у нас из раздела конфигурации по сайтам). Специально для кожаных мешков, так как сами DC определяют друг друга по следующему свойству.

  • LastOriginatingChangeDirectoryServerInvocationId — уникальный идентификатор контроллера домена. InvocationID можно узнать с помощью Get‑ADDomainController.

  • Version — сколько раз менялось это свойство.

Get-ADReplicationAttributeMetadata "CN=Oleg Petrov,OU=Test OU,DC=trytopwn,DC=me" -Server winad.trytopwn.me -IncludeDeletedObjects -ShowAllLinkedValues | where-object {$_.LocalChangeUsn -gt 225651}
Свойство, которое поменялось у пользователя Oleg Petrov
Свойство, которое поменялось у пользователя Oleg Petrov

Именно по LocalChangeUsn мы и будем определять свойства, которые были изменены. Достаточно просто вывести параметры, у которых LocalChangeUsn больше нашего старого значения USN.

Здесь стоит сделать небольшую ремарку и рассказать про Originating и Replicated изменения. Когда мы изменяем свойства объектов в AD, это всегда происходит на каком-то конкретном контроллере домена. Предположим, что у нас хотя бы 2 контроллера домена и изменения могли быть на любом из них. В таком разрезе у нас есть два типа обновлений, которые получает каждый контроллер:

  • Originating Update — первичное изменение, которое контроллер не получил через репликацию, а оно произошло на нем самом. Слово Originating часто появляется в названии разных свойств объектов, намекая на исходный DC, на котором были сделаны изменения.

  • Replicated Update — изменения, которые контроллер получил с помощью репликации от других контроллеров.

Теперь у нас есть вся цепочка, чтобы точечно отслеживать изменения: выбираем DC > выясняем его локальный USN > ждем изменения этого USN > запрашиваем объекты с USNChanged больше старого значения USN > смотрим свойства объектов, у которых LocalChangeUsn больше старого значения USN > ждем следующего изменения USN.

Справедливости ради стоит сказать, что не все свойства мы таким образом можем отслеживать. Есть перечень свойств, которые не реплицируются между DC. Это определяется через атрибут FLAG_ATTR_NOT_REPLICATED (вернее, через атрибут systemFlags) у свойств в схеме Active Directory (подробнее можно почитать тут). Проще говоря, если systemFlags у свойства является нечетным, то такой объект не реплицируется между DC (там есть еще значение для репликации в глобальный каталог, но туда не полезем сегодня). Команду для вывода списка нереплицируемых свойств можно найти ниже.

Get-ADObject -Filter * -SearchBase "CN=Schema,CN=Configuration,DC=trytopwn,DC=me" -server winad.trytopwn.me -Properties systemFlags,lDAPDisplayName | where-object {($_.systemflags%2) -eq 1} | select-object Name,lDAPDisplayName,systemflags | sort-object Name
Вывод списка нереплицируемых свойств.
Вывод списка нереплицируемых свойств.

Еще стоит отметить, что не для каждого свойства значения хранятся в готовом виде. Некоторые свойства дублируют информацию друг друга. По этой причине только одна часть подобной информации реплицируется. Возьмем для примера свойства Members у объекта «группа» и MemberOf у пользователей/компьютеров/групп. Members является так называемым forward link attribute, который хранит информацию и реплицируется между DC. MemberOf, в свою очередь, является backward link attribute, который высчитывается на основе значений Member разных групп. И данное свойство не реплицируется.

Зачем нам это все знать? Если при отслеживании изменений с помощью репликации какой-то пользователь был добавлен или удален из группы, то вы увидите изменение только на объекте группы, а не на пользователе. Кстати, с помощью запроса ниже можно увидеть, какие атрибуты связаны друг с другом через свойство LinkID (подробнее можно почитать тут).

Зачем описывать то, что уже хорошо описали.
Зачем описывать то, что уже хорошо описали.
Get-ADObject -Filter * -SearchBase "CN=Schema,CN=Configuration,DC=trytopwn,DC=me" -server winad.trytopwn.me -Properties systemFlags,lDAPDisplayName,linkID | where-object {$_.LinkID} | select-object Name,lDAPDisplayName,linkID,systemFlags | sort-object LinkID
Связанные аттрибуты forward & backward link. Обратите внимение на systemFlags.
Связанные аттрибуты forward & backward link. Обратите внимение на systemFlags.

А теперь вспомним нюансы при использовании утилит а-ля Get-Adobject -Filter * и посмотрим, что меняется при новом подходе:

  • Количество существующих объектов в домене здесь играет намного меньшую роль, так как на постоянной основе мы следим только за 1 свойством 1 объекта Active Directory. По этой причине можно проверять данное свойство хоть каждые 10 секунд. Такие запросы, как правило, не мониторятся средствами защиты. Но даже если мы захотим стать тише (или уменьшить нагрузку на сеть), то всегда можем поставить задержку больше. Например, 60 секунд. И делать это можно, не боясь потерять изменения или информацию о них.

  • Так как мы ориентируемся на счетчик изменений, увеличение времени проверки USN, как максимум, может привести к тому, что мы пропустим значения свойств, которые сразу перезаписались (например, если внесли что-то в описание, применили, увидели ошибку, исправили, и все это в рамках 10 секунд). Но сам факт изменения мы все же увидим, так как USN изменился, USNChanged у объекта и LocalChangeUsn у свойства - тоже. К тому же, по номеру версии мы можем увидеть, сколько раз свойство поменялось. То есть, так мы не пропустим сам факт того же включения/выключения учетки.

  • Опять же, так как мы ориентирумся на счетчик USN, нам не надо сравнивать значения свойств. Поэтому, даже если свойство менялось 4 раза и в итоге вернулось к исходному значению, мы все равно увидим эту активность (USN и Version изменились). А значит, изменения свойств с именами других объектов в значении (например, nTSecurityDescriptor) мы также сможем увидеть.

Если собрать все вместе, то можно получить инструмент, который будет отображать любые изменения в Active Directory с минимальной загрузкой сети и контроллера домена. Мой инструмент получил название ADSpider (к слову, это не единственная утилита, которая использует подобный механизм).

Пример вывода ADSpider на тестовом домене. Изменения ACL OU здесь уже нам показывается.
Пример вывода ADSpider на тестовом домене. Изменения ACL OU здесь уже нам показывается.
Использование сети на DC при запуске ADSpider c хоста DESKTOP-5PHFK5J. Сравните с картинкой от LDAPMonitor
Использование сети на DC при запуске ADSpider c хоста DESKTOP-5PHFK5J.
Сравните с картинкой от LDAPMonitor

Кроме обычного отслеживания, в нем есть несколько удобных (для меня) моментов:

  • можно выбрать задержку, с которой будет запрашиваться USN. По умолчанию задержка равна 30 секундам;

  • каждое изменение сохраняется; при повторном изменении того же свойства у объекта будет отображаться не только новое свойство (зеленый цвет), но и предыдущее (желтый цвет);

  • перед запуском можно сдампить всю базу AD, и тогда любые изменения будут сопровождаться предыдущими версиями (да, дамп делается через всеми любимый Get-ADObject -Filter * -Properties *. На больших AD запускайте на свой страх и риск);

  • если какой-то объект слишком часто мозолит глаза, его можно добавить в исключение по GUID;

  • можно указать исходный USN, если вдруг что то пропустили;

  • вывод можно сохранить в xml файл. Сохраненный файл можно также импортировать для красивого вывода информации;

  • в поле Explanation выводится информация, если нужно какое-то пояснение для измененного свойства (например, userAccountControl). Со временем добавятся расшифровки для других свойств .

Конечно, в академическом плане это все интересно (надеюсь, вам тоже), но какой в этом практический смысл? Сначала посмотрим со стороны атакующих.

Из очевидных моментов: пароли в описании (или еще где-нибудь), включение/выключение разных УЗ (была ситуация, когда административные УЗ у безопасников включались на некоторое время под определенные задачи), небезопасные изменения ACL (делегирование прав для «региональных» админов).

Из не таких очевидных: как говорится, «бывших не бывает», и на внутренних тестированиях я всегда пытаюсь понять, как работают сисадмины. Как у них структурирована AD, и как там создаются объекты, какими инструментами пользуются, где папка ИТ отдела с их софтом, скриптами и текстовыми файлами. Все это позволяет найти легитимные пути администрирования, чтобы определить их слабые места и потом использовать (коллеги администраторы, подумайте об этом :). Наблюдение изменений AD тоже может дать пищу для размышлений в этом направлении (извините, что получилось немного абстрактно).

С точки зрения защиты все очевиднее: отслеживать изменения в AD очень важно. В мою бытность администратором проблема решалась включением сбора расширенных журналов событий на всех DC. Далее аккумулируем и парсим эту информацию (если есть еще какие-то варианты, напишите, пожалуйста. Время-то идет). Но под это дело надо расширять файлы журналов, дополнительное место, вот это вот все. На мой взгляд, отслеживание через репликацию намного проще. А если вывод перенаправить в какую-нибудь софтину для мониторинга, то можно малой кровью отслеживать и реагировать на разные странные события в AD.

К слову сказать, на одном из проектов через подобное отслеживание удалось увидеть свою же эксплуатацию RBCD на контроллер домена. Интересно, что Заказчик этого действия не увидел вообще.

Сам себя поймал. Прям как котик.
Сам себя поймал. Прям как котик.

В статье я не ставил задачу глубоко погрузиться в репликацию Active Directory. Вернее, сначала ставил, но, когда не смог за долгое время этого достичь, решил написать только ту информацию, которая относится к созданному инструменту. Если кому-то хочется погрузиться, то вот прекрасный deep dive по этой тематике (хотя не уверен, что за 10 лет изменений не было совсем).

Читая про механизмы репликации Active Directory в прекрасной книге с котиками (еще аж про Windows Server 2012), я и подумать не мог о возможном варианте применения этих знаний. По большей части это изучалось для академических целей и для каких-то нестандатрных моментов в администрировании. Для меня это хороший аргумент в сторону того, что чтение и изучение внутренних механизмов известного ПО может дать очень интересные результаты. Даже если на момент изучения еще нет понимания, что с этими знаниями делать.

Комментарии (6)


  1. AntonyN0p
    11.07.2024 13:31

    Спасибо автору за хороший материал! Отслеживание изменений в AD путём сравнения текущего слепка с baseline image - годная тема при реагировании и расследовании инцидентов =)


  1. wmlab
    11.07.2024 13:31

    А я в свое время забирал изменения в AD из лога аудита. Надо только его настроить.


    1. b4444
      11.07.2024 13:31

      Категорически согласен. А потом пересылать в graylog, а там уже нужные оповещения вешать или просто хранить историю. Особенно, когда много доменов и контроллеров "в подчинении"


  1. grumbler70
    11.07.2024 13:31

    Спасибо, вот и есть это настоящий Хабр!

    Интересно, а создатели всяких FreeIPA и клонов, они "изобретали велосипед", или всё же подсмотрели способ реализации репликации?


  1. Wesha
    11.07.2024 13:31
    +2

    Паук в Active Directory так лапками тыдык тыдык

    Не могло не напомнить...


    1. uksus Автор
      11.07.2024 13:31

      В большинстве случаев так и решается все :))