В работе администратора домена Active Directory довольно часто возникает необходимость найти причину блокировки пользователя. Иногда причиной блокировки пользователя является заражённый вирусом ПК — в таких случаях особо важна скорость обнаружения источника проблемы. В PowerShell 2 на Windows 2008 R2 есть прекрасный командлет Get-WinEvent который позволяет решать данную задачу за 1-2 минуты даже в очень большом домене.
Примечание: всё далее описанное касается только PowerShell >= 2.0
Необходимые исходные условия.
Да, чудес не бывает, и для поиска причины блокировки пользователей потребуется некоторая настройка аудита безопасности на контроллерах домена. Должны быть включены следующие подкатегории аудита на контроллерах домена:
Категория аудита | Подкатегория аудита | Тип аудита | Интересующий нас eventID |
---|---|---|---|
Audit Account Logon Events (Вход учётной записи) | Kerberos Authentication Service (Аудит службы проверки подлинности Kerberos) | Отказ | 4771 Kerberos pre-authentication failed. |
Audit Logon Events (Вход/Выход) | Logon (Аудит входа в систему) | Отказ | 4625 An account failed to log on. |
Audit Logon Events (Вход/Выход) | Account Lockout (Аудит блокировки учётных записей) | Успех | 4740 Account locked |
Алгоритм поиска источника блокировки
- Определить какой контроллер является владельцем роли PDC-эмулятора
- Найти в журнале безопасности PDC-эмулятора событие с кодом 4740 в поле «TargetUserName», которого указано имя интересующего нас пользователя
- В поле «TargetDomainName» того же события найти имя контроллера домена, обслужившего авторизацию
- Найти в журнале безопасности контроллера, обслужившего авторизацию, событие с кодом 4625 (неуспешная NTLM авторизация) или 4771 (неуспешная kerberos авторизация)
- Из найденного события получить значения полей «IpAddress» — адрес ПК с которого была попытка авторизации
Этих действий будет достаточно для быстрого поиска ПК с вредоносным ПО, подбирающим пароли пользователей. Как правило достаточно быстро вычислить источник проблемы и изолировать его для дальнейшего расследования. Алгоритм поиска процесса непосредственно вызывающего блокировку пользователя сильно зависит от ПО, установленного на конечном ПК.
Используемые командлеты PowerShell
- Get-ADDomainController — для определения владельца роли PDC-эмулятора
- Get-WinEvent — «волшебный» командлет, благодаря которому и стал возможным быстрый поиск
В чём «магия» Get-WinEvent?
Во-первых он ищет события с конца и имеет параметр -MaxEvents, позволяющий найти только самое последнее событие или неколько самых последних событий.
Во-вторых он имеет прекрасный параметр -FilterHashtable, который позволяет очень гибко задать условия поиска события.
Скрипт поиска
Для себя я подготовил небольшой PowerShell скрипт.
На входе скрипт принимает либо имя пользователя, для которого необходимо найти источник блокировки, либо количество (по умолчанию — 1) последних заблокированных пользователей.
param (
$User,
$Count = 1
)
$result = New-Object system.Data.DataTable "Locks"
$col1 = New-Object system.Data.DataColumn Username,([string])
$col2 = New-Object system.Data.DataColumn DCFrom,([string])
$col3 = New-Object system.Data.DataColumn LockTime,([string])
$col4 = New-Object system.Data.DataColumn eventID,([string])
$col5 = New-Object system.Data.DataColumn SourceHost,([string])
$col6 = New-Object system.Data.DataColumn LogonType,([string])
$col7 = New-Object system.Data.DataColumn LogonProcessName,([string])
$col8 = New-Object system.Data.DataColumn FalureTime,([string])
$result.columns.add($col1)
$result.columns.add($col2)
$result.columns.add($col3)
$result.columns.add($col4)
$result.columns.add($col5)
$result.columns.add($col6)
$result.columns.add($col7)
$result.columns.add($col8)
$PDC = [string](Get-ADDomainController -Discover -Service PrimaryDC).Hostname
$FilterHash = @{}
$FilterHash.LogName = "Security"
$FilterHash.ID = "4740"
if ($User) {
$FilterHash.data =$User
$Count = 1
}
$FilterHash2 = @{}
$FilterHash2.LogName = "Security"
$FilterHash2.ID = @("4625", "4771")
Get-WinEvent -Computername $PDC -FilterHashtable $FilterHash -MaxEvents $Count | foreach {
$Row = $result.NewRow()
$Username = ([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq “TargetUserName”} | %{$_."#text"}
$DCFrom = ([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq “TargetDomainName”} | %{$_."#text"}
$LockTime = $_.TimeCreated
$FilterHash2.data = $username
Get-WinEvent -Computername $dcfrom -FilterHashtable $FilterHash2 -MaxEvents 3 | foreach {
$Row = $result.NewRow()
$Row.Username = $Username
$Row.DCFrom = $DCFrom
$Row.LockTime = $LockTime
$Row.eventID = $_.ID
$Row.SourceHost = ([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq “IpAddress”} | %{$_."#text"}
$Row.LogonType = ([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq “LogonType”} | %{$_."#text"}
switch ($Row.LogonType)
{
"2" {$Row.LogonType = "Interactive"}
"3" {$Row.LogonType = "Network"}
"4" {$Row.LogonType = "Batch"}
"5" {$Row.LogonType = "Service"}
"7" {$Row.LogonType = "Unlock"}
"8" {$Row.LogonType = "NetworkCleartext"}
"9" {$Row.LogonType = "NewCredentials"}
"10" {$Row.LogonType = "RemoteInteractive"}
"11" {$Row.LogonType = "CachedInteractive"}
}
$Row.LogonProcessName = ([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq “LogonProcessName”} | %{$_."#text"}
$Row.FalureTime = $_.TimeCreated
$result.Rows.Add($Row)
}
}
$result | Format-Table -AutoSize
Пример использования:
PS> Find-Locker.ps1 -User i.i.inanov
PS> Find-Locker.ps1 -Count 10
Вместо P.S.
Да, есть Account Lockout and Management Tools с прекрасной утилитой LockoutStatus.exe, но она долго опрашивает все контроллеры домена (а некоторый из них могут быть и недоступны).
Предложенный скрипт никак не претендует на замену LockoutStatus.exe (у них совершенно разный принцип работы). Обычно я использую их вместе.
Готовый скрипт можно скачать с Google Drive: Find-Locker.ps1
Автор: Slipeer