15 декабря 2011
Использование WMI для мониторинга учетных записей
Статья предназначена, прежде всего, для системных администраторов и специалистов по информационной безопасности, имеет практическую направленность. Предполагается, что читатель знаком с основами программирования, а также обладает навыками работы в табличном редакторе Excel/Calc.
WMI сокращение от Windows Management Instrumentation (инструментарий управления Windows). WMI — реализация корпорацией Microsoft протокола WBEM, который устанавливает стандарты доступа к данным управления по сети предприятия. Более подробное описание, историю развития, общую структуру и описание объектов можно найти на сайте Microsoft1 или в других источниках2 3.
Поставим задачи, которые будем решать:
- упрощение мониторинга состояния учетных записей в Active Directory;
- определение и корректировка прав пользователей на компьютерах в сети.
Для наглядности будем использовать доменное имя example.com. Полагаем, что корпоративные правила предписывают использование ГОСТ 7.79-2000 (ИСО 9-95) ПРАВИЛА ТРАНСЛИТЕРАЦИИ КИРИЛЛОВСКОГО ПИСЬМА ЛАТИНСКИМ АЛФАВИТОМ, Система Б — транслитерация с использованием буквосочетаний4, это является хорошей практикой и помогает автоматизировать транслитерацию. Компьютеры в сети имеют наименования удовлетворяющие ограничениям NetBIOS и состоящие из двух частей, соединенных тире: наименование подразделения и инвентарный номер. Учетные записи пользователей имеют формат «фамилия_ио». Например, компьютер бухгалтерии — buxg-002701, учетная запись сотрудника — kuzneczova_na. Распространенной практикой является создание специальной учетной записи для сотрудников подразделения безопасности, которая включена в группу локальных администраторов на всех компьютерах сотрудников.
В статье будут использоваться Visual Basic Script и табличный редактор Calc из состава OpenOffice.org, не сложно самостоятельно перевести приведенные ниже скрипты на привычный для читателя язык программирования и обработать полученные данные. Скрипт можно запускать от имени любой учетной записи пользователя Active Directory независимо от его привилегий. В это есть как преимущества — для проведения анализа не нужна специальная подготовка, так и недостатки — любой человек, который имеет учетную запись в Active Directory, может провести описанные далее действия самостоятельно или по чьему-то поручению.
Для упрощения мониторинга состояния учетных записей в Active Directory необходимо, прежде всего, получить список учетных записей и их атрибуты в удобной форме. Напишем скрипт, с помощью которого получим информацию о компьютерах и пользователях домена в двух текстовых файлах.
**********
ScanFromLDAP.vbs
**********
' в случае ошибок пробуем продолжить выполнение скрипта
On Error Resume Next
' объявляем константы и переменные
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20
Const ADS_SCOPE_SUBTREE = 5
workDomain = "DC=example,DC=com"
workDomain_= "example.com"
' создаем файлы, куда будем сохранять информацию о компьютерах и пользователях домена
Set fsoComp = CreateObject("Scripting.FileSystemObject")
Set fileComp = fsoComp.OpenTextFile("comp_" & workDomain_ & "_.txt", 2, True)
Set fsoUsr = CreateObject("Scripting.FileSystemObject")
Set fileUsr = fsoUsr.OpenTextFile("usr_" & workDomain_ & "_.txt", 2, True)
' получаем данные о компьютерах
Set objConnectionComp = CreateObject("ADODB.Connection")
Set objCommandComp = CreateObject("ADODB.Command")
objConnectionComp.Provider = "ADsDSOObject"
objConnectionComp.Open "Active Directory Provider"
Set objCOmmandComp.ActiveConnection = objConnectionComp
objCommandComp.Properties("Page Size") = 1000
objCommandComp.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommandComp.CommandText = _
"Select " _
& " CN, " _
& " Name, " _
& " DisplayName, " _
& " DNSHostName, " _
& " Description, " _
& " Location, " _
& " WhenCreated, " _
& " WhenChanged, " _
& " LogonCount, " _
& " OperatingSystem, " _
& " OperatingSystemServicePack, " _
& " OperatingSystemVersion, " _
& " UserAccountControl, " _
& " DistinguishedName " _
& " from 'LDAP://" & workDomain & "' Where objectCategory='computer'"
Set objRecordSetComp = objCommandComp.Execute
objRecordSetComp.MoveFirst
' записываем в файл заголовок для данных
fileComp.WriteLine( "CN" & vbTab & _
"Name" & vbTab & _
"DisplayName" & vbTab & _
"DNSHostName" & vbTab & _
"Description" & vbTab & _
"Location" & vbTab & _
"Created" & vbTab & _
"Changed" & vbTab & _
"LogonCount" & vbTab & _
"OperatingSystem" & vbTab & _
"OperatingSystemServicePack" & vbTab & _
"OperatingSystemVersion" & vbTab & _
"UserAccountControl" & vbTab & _
"ACCOUNTDISABLE" & vbTab & _
"DistinguishedName")
' перебираем в цикле записи о всех компьютерах
Do Until objRecordSetComp.EOF
' записываем в файл данные о компьютере
fileComp.WriteLine( (objRecordSetComp.Fields("CN").Value) & vbTab _
& (objRecordSetComp.Fields("Name").Value) & vbTab _
& (objRecordSetComp.Fields("DisplayName").Value) & vbTab _
& (objRecordSetComp.Fields("DNSHostName").Value) & vbTab _
& (objRecordSetComp.Fields("Description").Value) & vbTab _
& (objRecordSetComp.Fields("Location").Value) & vbTab _
& (objRecordSetComp.Fields("WhenCreated").Value) & vbTab _
& (objRecordSetComp.Fields("WhenChanged").Value) & vbTab _
& (objRecordSetComp.Fields("LogonCount").Value) & vbTab _
& (objRecordSetComp.Fields("OperatingSystem").Value) & vbTab _
& (objRecordSetComp.Fields("OperatingSystemServicePack").Value) & vbTab _
& (objRecordSetComp.Fields("OperatingSystemVersion").Value) & vbTab _
& (objRecordSetComp.Fields("UserAccountControl").Value) & vbTab _
& CStr((CLng(objRecordSetComp.Fields("UserAccountControl").Value) and 2)/2) & vbTab _
& (objRecordSetComp.Fields("DistinguishedName").Value))
objRecordSetComp.MoveNext
Loop
' получаем данные о пользователях
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCOmmand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.CommandText = _
"Select " _
& " CN, " _
& " Name, " _
& " DisplayName, " _
& " sAMAccountName, " _
& " UserPrincipalName, " _
& " Description, " _
& " Location, " _
& " WhenCreated, " _
& " WhenChanged, " _
& " LogonCount, " _
& " ScriptPath, " _
& " UserAccountControl, " _
& " MemberOf, " _
& " DistinguishedName " _
& " from 'LDAP://" & workDomain & "' Where objectCategory='user'"
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
' записываем в файл заголовок для данных
fileUsr.WriteLine( "CN" & vbTab & _
"Name" & vbTab & _
"DisplayName" & vbTab & _
"sAMAccountName" & vbTab & _
"UserPrincipalName" & vbTab & _
"Description" & vbTab & _
"Location" & vbTab & _
"Created" & vbTab & _
"Changed" & vbTab & _
"LogonCount" & vbTab & _
"ScriptPath" & vbTab & _
"UserAccountControl" & vbTab & _
"ACCOUNTDISABLE" & vbTab & _
"NORMAL_ACCOUNT" & vbTab & _
"MemberOf" & vbTab & _
"DistinguishedName")
' перебираем в цикле записи о всех пользователях
Do Until objRecordSet.EOF
' обрабатываем поля, которые могут содержать список или Null
if IsNull(objRecordSet.Fields("Description").Value) then
str_description = ""
else
str_description = Join((objRecordSet.Fields("Description").Value), ",")
end if
if IsNull(objRecordSet.Fields("MemberOf").Value) then
str_MemberOf = ""
else
str_MemberOf = Join((objRecordSet.Fields("MemberOf").Value), ",")
end if
' записываем в файл данные о пользователе
fileUsr.WriteLine((objRecordSet.Fields("CN").Value) & vbTab _
& (objRecordSet.Fields("Name").Value) & vbTab _
& (objRecordSet.Fields("DisplayName").Value) & vbTab _
& (objRecordSet.Fields("sAMAccountName").Value) & vbTab _
& (objRecordSet.Fields("UserPrincipalName").Value) & vbTab _
& str_description & vbTab _
& (objRecordSet.Fields("Location").Value) & vbTab _
& (objRecordSet.Fields("WhenCreated").Value) & vbTab _
& (objRecordSet.Fields("WhenChanged").Value) & vbTab _
& (objRecordSet.Fields("LogonCount").Value) & vbTab _
& (objRecordSet.Fields("ScriptPath").Value) & vbTab _
& (objRecordSet.Fields("UserAccountControl").Value) & vbTab _
& CStr((CLng(objRecordSet.Fields("UserAccountControl").Value) and 2)/2) & vbTab _
& CStr((CLng(objRecordSet.Fields("UserAccountControl").Value) and 512)/512) & vbTab _
& MemberOf & vbTab _
& (objRecordSet.Fields("DistinguishedName").Value))
objRecordSet.MoveNext
Loop
' закрываем файлы
fileUsr.Close
fileComp.Close
Wscript.Echo "ScanFromLDAP.vbs выполнен для - " & workDomain_
' перевод даты в "нормальный" вид
Function WMIDateStringToDate(dtmDate)
WMIDateStringToDate = " "
if not IsEmpty(dtmDate) then
WMIDateStringToDate = CDate(Mid(dtmDate, 5, 2) & "/" & _
Mid(dtmDate, 7, 2) & "/" & Left(dtmDate, 4) _
& " " & Mid (dtmDate, 9, 2) & ":" & Mid(dtmDate, 11, 2) & ":" & Mid(dtmDate,13, 2))
end if
End Function
**********
Открываем полученные файлы в Calc (разделитель — знак табуляции, кодировка win-1251), накладываем автофильтр на верхнюю строку с заголовками (выделяем первую строку, меню Данные — Фильтр — Автофильтр) и приступаем к анализу. Для уменьшения объема данных можно удалить строки с заблокированными записями (колонка ACCOUNTDISABLE=1), но лучше сделать это позже.
Вначале проверяем соответствие имен компьютеров и учетных записей принятым в организации форматам. На этом этапе чаще всего можно определить записи об объектах, которые планировались как временные, или об объектах не относящихся к работе, такие как gameserv, ivanoff, temp1. В полях Description и Name/DisplayName могут встречаться комментарии вида «удалить потом», «лишняя?», «шаблон», на которые следует обратить особое внимание. Практика показывает, что различные пометки часто выделяют скобками, искать удобно по вхождению скобок или других не типичных для наименований символов.
Разбирая данные из колонки DistinguishedName, для таблиц компьютеров и пользователей, можно получить списки всех OU (organizational unit, организационные единицы), которые, обычно, отражают структуру организации, взаимодействие подразделений. Многие колонки содержат полезные данные, смысл которых очевиден из их названия: описание (Description), время создания и изменения (Created, Changed), блокировка (ACCOUNTDISABLE) и другие.
Из таблицы с записями о компьютерах мы получим информацию о версиях операционных систем (OperatingSystem), наличии пакетов обновлений (OperatingSystemServicePack) и, соответственно, оценим потенциальны уязвимости, сможем планировать мероприятия по замене или обновлению.
Из таблицы с учетными записями пользователей получим информацию о скриптах, которые запускаются при входе в систему (ScriptPath), в какие группы входит пользователь (MemberOf). По запускаемым скриптам и группам мы можем оценить привилегии пользователя, функциональные обязанности, уровень доступа к массивам информации.
Можно выполнить сверку списков сотрудников и учетных записей. Это поможет выявить два важных момента: наличие действующих учетных записей для которых нет соответствия среди сотрудников и наличие пользователей без учетной записи, но которым по функциональным обязанностям необходима работа в сети. Задача упрощается, если при вводе пользователей поле DisplayName заполняется полным ФИО, т.к. в данном случае отсутствуют дополнительные преобразования, достаточно проверить пересечение списков. Если в организации есть установленные правила транслитерации, например уже упоминавшийся ГОСТ 7.79-2000, то возможно выполнить преобразование (транслитерацию и приведение к формату) списка ФИО сотрудников и сравнить с колонкой sAMAccountName.
Для определения и корректировки прав пользователей необходимо собрать информацию о том, в какие группы входят учетные записи на компьютерах в сети. Напишем скрипт, с помощью которого выполним сканирование компьютеров из базы Active Directory.
**********
CompGroupUser.vbs
**********
' в случае ошибок пробуем продолжить выполнение скрипта
On Error Resume Next
' объявляем константы и переменные
Const ADS_SCOPE_SUBTREE = 2
Dim fso
Dim file
' создаем и открывает текстовый файл для сохранения результатов
Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.OpenTextFile("CompGroupUser.txt", 2, True)
' подключаемся к домену и выбираем компьютеры
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.CommandText = "Select Name from 'LDAP://DC=example,DC=com' " _
& "Where objectClass ='computer'"
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
' перебираем все записи (имена компьютеров, зарегистрированных домене)
Do Until objRecordSet.EOF
' вызываем процедуру, которая обрабатывает конкретный компьютер
MyCGU(objRecordSet.Fields("Name").Value)
objRecordSet.MoveNext
Loop
file.Close
Wscript.Echo "Скрипт CompGroupUser выполнен"
' процедура, получающая группы и пользователей, сохраняет в файл
Sub MyCGU (strComputerName)
' подсключаемся к компьютеру и получаем все группы
Set colGroups = GetObject("WinNT://" & strComputerName & ".example.com")
colGroups.Filter = Array("group")
' перебираем все группы и всех пользователей в них
For Each objGroup In colGroups
For Each objUser in objGroup.Members
file.WriteLine(strComputerName & vbTab & objGroup.Name & vbTab & objUser.Name)
Next
Next
End Sub
**********
В результате работы скрипта получаем текстовый файл с тремя колонками: Компьютер, Группа пользователей, Пользователь. Колонок мало, содержание данных очевидно, поэтому скрипт не включает заголовки в текстовый файл.
Небольшой недостаток предложенного метода — сложность определения по имени пользователя является он локальным для компьютера или пользователем Active Directory. Частично этот недостаток преодолевается сравнением со списком всех пользователей Active Directory, который получен с помощью предыдущего скрипта.
Скрипт получает информацию непосредственно с компьютеров, если во время сканирования компьютер будет отключен, информация о нем собрана не будет. Для улучшения результата скрипт можно запустить несколько раз и объединить полученные текстовые файлы. Объединенные данные необходимо очистить от дублирования, для этого используем Calc (выделяем все три колонки, меню Данные — Фильтр — Расширенный фильтр, выбираем опцию Без повторений).
Анализ полученных данных позволяет, прежде всего, выявить пользователей, которые обладают правами локальных администраторов, проверить обоснованность предоставления административных прав. Для этого необходимо удалить разрешенные учетные записи администраторов, такие как «Администратор», «for_security», «for_scripts», и автофильтром оставить только группу «Администраторы».
Аналогично можно выявить пользователей, которые входят в другие специфические группы, предоставляющие дополнительные привилегии, такие как «Группа удаленных помощников», «Пользователи удаленного рабочего стола» и др.
Выполнить пересечение всего списка компьютеров и списка компьютеров, где есть пользователь «for_security» поможет выявить те машины, где учетная запись для сотрудников службы безопасности отсутствует. Причины отсутствия учетных записей службы безопасности могут быть различные, но наиболее часто встречаются две: сознательное удаление и сбой работы групповых политик. Оперативная реакция позволяет сохранить контроль на системой в целом.
Отдельно отметим возможность выявить некоторое программное обеспечение, установленное на компьютерах сети, по появлению специфических групп пользователей. Примеры шаблонов специфических групп: «*vmware*», «*acronis*», «*SQLServer*».
1 http://technet.microsoft.com/ru-ru/library/cc787057(WS.10).aspx
2 http://ru.wikipedia.org/wiki/WMI
3 http://www.script-coding.com/WMI.html
4 http://www.gsnti-norms.ru/norms/common/doc.asp?2&/norms/stands/7_79.htm