Используя групповые политики Active Directory можно настроить аудит смены паролей и других действий связанные с пользователями. Эти события можно получить используя оснастку Event Viewer и Powershell. В этой статье мы разберем эти возможности на примерах и создадим команду Powershell, которая сделает этот процесс более простым.
Настройка политики аудита
Самый удобный способ создать политику - использовать оснастку 'Group Policy Management'. Эта оснастка может быть открыта через RSAT или на домен-контроллер. Оснастку групповых политик так же открывается командой:
gpedit.msc
Вам необходимо выбрать OU, где находятся компьютеры, и создать в ней новую политику. Можно выбрать так же сайт или домен если вы планируете охватить область большую чем OU:
В примерах я использую OU Moscow. Через Powershell создать и привязать политику тоже можно. Это делается следующим образом:
# Создаем политику
$gpo = New-GPO -Name 'PasswordAudit'
# Соединяем политику с OU Moscow
New-GPLink -Name 'PasswordAudit' -Target 'OU=Moscow,DC=domain,DC=local'
Политика, которая нам нужна, называется 'Audit account management/Аудит управления учетной записью'. Она включает аудит связанный с изменением пользователя, пароля и групп. Что бы это сделать пройдите по следующему пути
- 'Computer Configuration' - 'Policies' - 'Windows Settings' - 'Security Settings' - 'Local Policies' = 'Audit Policy';
- 'Конфигурация компьютера' - 'Параметры Windows' - 'Параметры безопасности' - 'Локальные политики' - 'Политика аудита'.
В этой политике нужно включить учет успешных попыток изменения данных (Success) и провала (Failure):
Powershell плохо предназначен для настройки политик. Основная проблема в том, что в качестве пути до политики мы должны указывать ветку реестра и знать значения, которые планируем менять. Политики аудита, связанные с безопасностью, не имеют веток реестра вообще и из-за этого установить их командами Powershell не возможно.
Ниже, для примера, показано как через Powershell устанавливается политика включающая заставку через 900 секунд:
# Set-GPRegistryValue -Name "Созданная политика" -Key "HKCU\Software\Policies\Microsoft\Windows\Control Panel\Desktop" -ValueName "ScreenSaveTimeOut" -Type DWORD -Value 900
У домен контроллеров есть роль FSMO, которая отвечает за пароли. Ее название PDC - 'Primary domain controller'. На домен контроллере, который держит эту роль, и будут собираться все события связанные с политикой аудита. Увидеть кому принадлежит эта роль можно так:
(Get-ADDomain).PDCEmulator
На клиентских компьютерах запустим обновление политик:
Invoke-GPUpdate -Force -Target 'Computer'
Получение событий
Учитывая, что включенная политика касается не только паролей, можно настроить фильтр через Powershell или GUI. Мы настроим фильтры, которые будут выводить события связанные с определенными идентификаторами 'Event ID'. Нас интересуют следующие идентификаторы:
- 4723 - попытка смены пароля;
- 4724 - попытка сброса пароля;
- 4740 - пользователь был заблокирован;
- 4767 - пользователь был разблокирован.
Фильтрация логов с Event Viewer
Что бы выполнить фильтрацию через GUI - откройте логи 'Security' в 'Event Viewer' на домен-контроллере. Открыв журнал вы увидите множество событий не касающихся паролей:
Эти фильтры мы можем применить зайдя в соответствующее меню:
В новом окне мы должны указать идентификаторы событий, их источник и категорию:
После этих установок у нас будут отображаться только нужные события.
Фильтрация логов с Powershell
В Powershell есть две команды для работы с логами:
- Get-WinEvent - получает логи из всех журналов;
- Get-Eventlog - получает логи из журналов Application, System, or Security. Является устаревшим командлетом.
Обе команды имеют параметр 'ComputerName', с помощью которого можно подключаться удаленно.
У получения логов средствами Powershell есть две проблемы:
- Процесс получения логов медленный. Выполнение одного запроса может выполняться больше 1 секунды;
- Если брать другие команды Powershell, то их вывод всегда структурирован в виде 'Ключ'='Значение'. При получении логов журнала, так же как в EventViewer, часть информации будет представлена как строка, т.е 'Ключ'= 'Ключ=Значение Ключ=Значение'. В разных случаях может придется использовать регулярные выражения и парсить значения.
Что бы максимально ускорить процесс получения логов можно использовать 3 параметра фильтрации: FilterXPath, FilterHashtable, FilterXml.
XPath - это язык запросов, который был создан для работы с XML. Мы можем не создавать свой запрос XPath, а увидеть уже составленный на созданном раннее фильтре, на вкладке XML:
Команда, которая получит подобные логи с домен-контроллера 'AD1' и используя запрос XPath, будет выглядеть следующим образом:
Get-WinEvent -ComputerName 'AD1' -LogName 'Security' -FilterXPath "*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and Task = 13824 and (EventID=4723 or EventID=4724 or EventID=4740 or EventID=4767)]]"
Аналогичное можно сделать использовав HashTable. Мы не должны указывать тип логов, т.к. это сделано в массиве:
$log = @{
LogName='Security';
ProviderName='Microsoft-Windows-Security-Auditing';
ID=4723,4724,4740;
}
Get-WinEvent -ComputerName 'AD1' -FilterHashtable $log
Если вам больше нравится вариант с запросом через hashtable, но вы не понимаете как правильно его составить, можно использовать следующий подход. Мы должны найти хоть один лог, который нам нужен используя любые средства Powerhsell. Затем мы выводим все свойства через 'SELECT *' и заполняем ими нашу таблицу:
Get-WinEvent -ComputerName 'AD1' -LogName 'Security' | where id -eq 4634 | select *
Отличия между массивом hashtable и обычными средствами Powershell в скорости. Если вы будете фильтровать вывод используя Powerhsell (как в примере выше) - это будет медленнее. Кроме указанных параметров в документации можно найти дополнительные варианты.
Создание команды
Создадим функцию с помощью который мы сможем выполнять эти действия более удобным для нас способом. Эта функция должна будет учитывать следующие моменты:
- возможность указания времени, указывающие на начало создания лога;
- понятный вывод сообщения соответствующий идентификаторам;
- источник и цель аудита. Например администратор изменивший пароль пользователя;
- тип события - success или fail.
Что бы получить события начинающиеся с определенной даты мы можем использовать существующее свойство 'StartTime' в массиве. 'EndTime' указывает обратное. В примере ниже мы получим события созданные за сутки:
# минус день от сегодняшней даты
$date = (Get-Date).AddDays(-1)
$hash = @{
LogName='Security';
ProviderName='Microsoft-Windows-Security-Auditing';
ID=4723,4724,4740;
StartTime=$date
}
Get-WinEvent -ComputerName 'AD1' -FilterHashtable $hash
Выше уже описывалась, что одна из проблем работы с журналом - это отсутствие форматирования каких-то данных. В примере ниже свойство 'Messages' хранит сплошной текст, а 'Properties' только некоторые отформатированные данные из 'Messages':
(Get-WinEvent -ComputerName 'AD1' -FilterHashtable $hash -MaxEvents 1).Message
(Get-WinEvent -ComputerName 'AD1' -FilterHashtable $hash -MaxEvents 1).Properties
Пример выше - скорее идеальный случай. У вас может отличаться порядок или другая информация в 'Properties'. Эти данные точно будут отличаться в логах с разными ID. В случае с одинаковыми ID - это так же может случатся.
Готовая функция, выводящая информацию об этих событиях, будет следующей:
Function Get-ADPasswordEvent {
[CmdletBinding()]
Param(
# массив с идентификаторами для проверки событий
[array]$EventID = @(4723,4724,4740,4767),
# поиск логов от указанной даты
[datetime]$BeginTime,
# тип события, которые мы ищем Удача/Провал
[ValidateSet("Success","Failure")]
[array]$EventType,
# IP/DNS имя удаленного компьютера
[string]$ComputerName
)
Process {
# составляем массив для поиска
$log_table = @{
LogName='Security';
ProviderName='Microsoft-Windows-Security-Auditing';
ID=$EventID;
}
# ниже проверяем какие параметры переданы пользователем
# и добавляем их в массив
# пользователь указал дату
if ($BeginTime){
$log_table += @{StartTime=$BeginTime}
}
# пользователю нужен только успех/провал
if ($EventType -eq 'Success'){
$log_table += @{Keywords=9007199254740992}
}
elseif ($EventType -eq 'Failure') {
$log_table += @{Keywords=4503599627370496}
}
$parameters = @{
FilterHashtable=$log_table
}
# если пользователь указал удаленный компьютер
if ($ComputerName){
$parameters += @{ComputerName=$ComputerName}
}
# передаем все параметры в команду
$events = Get-WinEvent @parameters
# массив, который вернем пользователю
# со всеми событиями
$formatted_events = @()
foreach ($event in $events){
# проверяем каждое событие
# и устанавливаем для него свое описание
Switch ($event.ID) {
4723 {
$Description = "Попытка изменения пароля"
Break
}
4724 {
$Description = "Попытка сброса пароля"
Break
}
4740 {
$Description = "Пользователь заблокирован"
Break
}
4767 {
$Description = "Пользователь разблокирован"
Break
}
Default {
# если его идентификатор не найден
$Description = $event.Message
Break
}
}
# определяем тип события
if ($event.KeywordsDisplayNames -like '*Success*'){$type = 'Успех'}
else {$type = 'Провал'}
# создаем массив текущего события
# и добавляем его к остальным найденным
$formatted_events += [PSCustomObject]@{
"Дата создания" = (Get-Date $event.TimeCreated -Format "dd-MM-yyyy HH:mm");
"ID" = $event.ID;
"Описание" = $Description;
"Тип" = $type;
"Цель (имя)" = $event.Properties[0].Value;
"Цель (компьютер)" = $event.Properties[1].Value;
"Кем (имя)" = $event.Properties[4].Value;
"Кем (компьютер)" = $event.Properties[5].Value;
"Цель (SID)" = $event.Properties[2].Value;
"Кем (SID)" = $event.Properties[3].Value;
}
}
# возвращаем составленный массив пользователю
return $formatted_events | ft
}
}
Примеры работы
Варианты использования функции:
# возвращаем все события по идентификаторам 4723,4724,4740,4767
Get-ADPasswordEvent
# возвращаем те же события, но за сутки
Get-ADPasswordEvent -BeginTime (Get-Date).AddDays(-1)
# возвращаем события провала с удаленного компьютера
Get-ADPasswordEvent -ComputerName 'AD1' -EventType 'Failure'
# возвращаем события используя другой идентификатор
Get-ADPasswordEvent -EventID 4724
...
Подписывайтесь на наш Telegram канал
Теги: #powershell #ad