Быстрый поиск в журналах службы безопасности

Управление множеством журналов, генерируемых в среде Windows, — необходимая, но отнимающая много времени задача администраторов. На каждой рабочей станции, автономном сервере и контроллере (DC) домена имеются журналы Security, Application и System, и во всех содержится ценная информация о системе и средствах обеспечения ее безопасности. В зависимости от среды и используемых компонентов Windows к ним могут добавиться журналы, генерируемые Internet Authentication Service (IAS), Microsoft IIS, RRAS и URLScan, а также журналы таких серверов приложений, как Microsoft Exchange Server и Microsoft SQL Server. Все эти журналы имеют различные форматы и структуры и содержат немало лишней информации, которую приходится удалять, чтобы отыскать важные события. Здесь требуется инструмент, который может читать и выполнять похожие на SQL запросы для журналов любых типов. Такой инструмент — LogParser, утилита командной строки, совместимая с Windows 2000 и более поздними версиями операционной системы. Возможности функций сбора данных LogParser не уступают такой базе данных SQL, как Microsoft Access. Инструмент автоматически обрабатывает мегабайты информации, записываемой в различные журналы сети предприятия. Во время подготовки данной статьи новейшей версией был LogParser 2.1. Продукт можно загрузить вместе с пакетом IIS 6.0 Resource Kit Tools (http://www.microsoft.com/downloads/details.aspx?familyid=56fc92ee-a71a-4c73-b628-ade629c89499&displaylang=en).

Как работает LogParser

LogParser состоит из трех основных компонентов: входного процессора, механизма обработки данных и выходного процессора. Входной процессор совместим с форматами таких журналов, как IIS и Windows (.evt-файлы). LogParser может читать файлы с разделительными запятыми (.csv), базы данных ODBC и текстовые файлы, в которых в качестве разделителя используются символы возврата каретки. Входной процессор преобразует данные журналов всех типов в единый формат, а затем информация обрабатывается в механизме данных LogParser точно так же, как таблицы в базе данных.

Администраторам, знакомым с операторами SQL SELECT, не составит труда освоить команды LogParser. В команде LogParser можно указать поля журнала, которые должны войти в генерируемый инструментом файл, полезные и ненужные записи о событиях (по результатам сравнения полей, заданных пользователем) и метод сортировки выходных записей. Но в действительности возможности LogParser гораздо шире. В механизме данных реализованы новейшие функции запросов, в частности подсчета записей, средних значений и определения 10 наиболее частых событий. Работая с текстовыми полями и производя вычисления над датами и числами, можно настраивать выходные данные и уточнять критерии выбора.

После того как механизм данных обработает информацию и выдаст набор результатов, в действие вступает выходной процессор, который преобразует набор результатов в выходную таблицу. Выходной процессор, как и входной, совместим со многими типами файлов, и выходную таблицу можно представить по-разному — от простых текстовых файлов до базы данных SQL или XML-файлов.

Простой запрос

Чтобы оценить возможности LogParser, начнем с примера команды и ее выходных данных. Следующая команда запрашивает журнал Security на локальной системе и генерирует отчет обо всех блокированных учетных записях:

logparser «SELECT DISTINCT SID
FROM security WHERE EventID = 644

Выходные данные приведены на экране 1. Здесь указаны лишь SID блокированных учетных записей пользователей; в дальнейшем я поясню, как преобразовать SID в имена пользователей. Как видно, LogParser — мощный инструмент с простым синтаксисом. Единственный обязательный агрумент — оператор SELECT, который необходимо заключить в кавычки. LogParser чувствителен к регистру символов при чтении большей части входной информации, поэтому, например, «eventid» отличается от «EventID». В зависимости от типа исследуемого журнала иногда необходимо указать параметры входных журналов или формат выходных файлов.

Формулировка запросов

Чтобы добиться от LogParser максимальной отдачи, необходимо научиться формулировать запросы. LogParser изначально совместим с журналами событий Windows, поэтому составлять запросы к журналу Security просто. LogParser не нуждается в дополнительных инструкциях по разбору журнала. Поэтому учиться составлять эффективные запросы удобно на примере журнала Security. Полученные знания можно применить и в обращении с журналами других типов.

Оператор SELECT состоит из двух обязательных (SELECT и FROM) и нескольких необязательных операторов:

SELECT выражение FROM выражение [TO
выражение] [WHERE выражение] [GROUP
BY выражение] [HAVING выражение]
[ORDER BY выражение]

Оператор SELECT указывает поля, которые войдут в каждую запись набора результатов запроса. Оператор FROM указывает, какой журнал или журналы следует использовать в качестве входных. Оператор TO задает место назначения для выходного файла. Оператор WHERE определяет критерии фильтрации входящих и выходящих записей запроса. Операторы GROUP BY и HAVING представляют собой мощные средства анализа групп аналогичных записей, общих вычислений над этими группами и задания критериев фильтрации групп на входе и выходе запроса. Оператор ORDER BY сортирует набор результатов по заданным полям. Рассмотрим операторы FROM, SELECT и WHERE более подробно.

FROM. Чтобы направить запрос к журналу Windows Security, следует использовать оператор FROM.

FROM security

Для обращения к другим стандартным журналам событий Windows «security» следует заменить на «application» или «system». Чтобы направить запрос к журналам других типов, необходимо указать имя журнала в операторе FROM.

SELECT. Следующий шаг после выбора опрашиваемого журнала при построении команды LogParser — подготовка предложения SELECT. Данный оператор задает список полей входного журнала, разделенных запятыми, которые войдут в выходной файл запроса. Благодаря встроенным функциям для работы с журналами событий Windows, LogParser автоматически распознает имена полей журнала (EventLog, RecordNumber, TimeGenerated, TimeWritten, Event-ID, EventType, EventTypeName, EventCategory, SourceName, Strings, ComputerName, SID и Message); во врезке «Поля журналов событий» кратко описано каждое поле. LogParser распознает имена полей журналов IIS. Для входных файлов других форматов LogParser может определить имена полей из первой строки журнального файла, если она представляет собой заголовок. В противном случае LogParser отмечает каждое входное поле как Field1, Field2 и т. д. В случае сомнений относительно имен полей их можно определить с помощью оператора SELECT * FROM filename LogParser выдает все поля из входного файла. После того как LogParser выдаст несколько записей, следует остановить запрос и просмотреть записи, чтобы определить назначение каждого поля, а затем сопоставить каждое поле имени, назначенному ему программой LogParser.

Оператор SELECT позволяет задавать выражения наряду с простыми именами полей. Пользователь может манипулировать строками и выполнять арифметические действия над числовыми полями и датами при генерации выходной таблицы. Например, выходную таблицу можно дополнить элементарным описанием каждого события, без всех строк, следующих за таким описанием. В журнале Security элементарное описание каждого события — первая информация, отображаемая в поле Message записи о событии; описание заканчивается двоеточием (:). Таким образом, LogParser должен исследовать поле Message, отыскать первое двоеточие и выдать текст, расположенный перед ним:

logparser «SELECT
SUBSTR(Message, 0,
INDEX_OF(Message, ?:?)) AS
Description FROM security»

В этой команде используются две функции манипулирования строками, реализованные в LogParser: SUBSTR и INDEX_OF. Найти начало поля Message (до первого двоеточия) можно с помощью функции SUBSTR. Первый параметр этой функции указывает исходную строку, которую предстоит разделить, — в данном случае Message. Второй параметр указывает позицию внутри исходной строки, с которой следует начать, например первый символ (позиция 0). Третий параметр указывает конечную позицию внутри исходной строки — двоеточие. Но чтобы получить позицию двоеточия, необходимо использовать функцию INDEX_OF, которая возвращает первую позицию символа «двоеточие» в поле Message. LogParser располагает множеством функций для манипуляций со строками, числами и датами; более подробную информацию об этих функциях можно найти в файле LogParser.doc в папке установки LogParser.

Чтобы дать выражению в запросе имя, следует использовать ключевое слово AS с последующим именем поля, которое будет отображаться в выходной таблице, например Description. Ключевое слово AS может потребоваться для именованных выражений в операторе SELECT или переименования поля в операторе SELECT. Ниже, в рассказе о подчиненных запросах, будет приведен пример использования AS для переименования полей.

Нередко LogParser выдает дублированные записи, так как несколько записей располагают одинаковыми значениями в наборе полей, указанных в операторе SELECT. Например, команда

logparser «SELECT EventID FROM security»

многократно сообщает о событиях ID 528 (успешная регистрация), каждое из которых соответствует успешной регистрации. Если нужен просто список всех идентификаторов ID зарегистрированных событий, то следует найти способ исключить дубликаты из набора результатов. В LogParser эта проблема решается с помощью ключевого слова DISTINCT. Чтобы исключить дублированные строки из набора результатов, достаточно вставить это ключевое слово после SELECT. Например, команда

logparser «SELECT DISTINCT EventID FROM security»

выдает список уникальных идентификаторов событий, представляющий перечень всех событий с уникальными ID, встречающихся в журнале хотя бы один раз. Ключевое слово DISTINCT применяется только к полям, указанным в операторе SELECT, но не к полям, указанным в других операторах, таких как WHERE, ORDER BY, GROUP BY или HAVING.

WHERE. После того как указан журнал или журналы, к которым нужно послать запросы, и поля, откуда требуется извлечь информацию, следует задать критерий фильтрации. Оператор WHERE задает выражение, имеющее значение TRUE или FALSE. Это выражение может быть простым:

«EventID = 529»

Или сложным, например:

«EventID = 529 AND
TimeGenerated >=
TO_TIMESTAMP(?2003-12-
21?,?yyyy-MM-dd?) AND
TimeGenerated <=
TO_TIMESTAMP(?2003-12-
24?,?yyyy-MM-dd?)»

Данное выражение извлекает все экземпляры события с ID 529 (неудачная попытка регистрации: неправильное имя пользователя или пароль), которые произошли между 21 декабря 2003 г. и 24 декабря 2004 г. (MM — месяц; mm — минуты). Несколько простых выражений внутри сложного выражения связаны оператором AND. Можно также использовать операторы OR, NOT и скобки, как в SQL или другом языке программирования.

Стандартные операторы сравнения SQL (табл. 1) можно задействовать в операторе WHERE для сравнения полей со значениями и выражениями. В частности, оператор LIKE очень полезен для сравнения с применением универсальных символов. Например, команда

logparser -i:EVT -resolveSIDs
ON «SELECT SID FROM security
WHERE SID LIKE ?%smith%?»

использует оператор LIKE для генерации списка всех событий, запущенных пользователями, в именах которых есть символы «smith» (например, Smith, Naismith, Smithe). Заменив «%smith%» на «Wri*», можно получить такие имена, как Wright и Wrigley; «%son» возвращает такие имена, как Johnson и Albertson. В этом примере команды использован еще один полезный параметр LogParser: -resolveSIDs. SID сам по себе довольно неудобен для восприятия, поэтому с помощью —resolveSIDs можно автоматически преобразовать SID в имена соответствующих учетных записей пользователей. Чтобы задействовать этот параметр, достаточно вставить перед оператором SELECT следующую запись:

-i:EVT -resolveSIDs ON

Например, команда

logparser -i:EVT -resolveSIDs

ON «SELECT DISTINCT SID FROM security»

может вывести результаты, показанные на экране 2. Аналогичным образом можно использовать функцию RESOLVE_SID() для преобразования SID в соответствующее имя учетной записи. Например, команда

logparser «SELECT DISTINCT SID,
RESOLVE_SID(SID) AS username
FROM security»

возвращает каждый уникальный SID и соответствующее имя пользователя (экран 3).

Обычно выходные данные фильтруются по значению поля относительно другого значения. Так, можно выяснить, совпадает ли ID события текущей записи с числом 529 или значение TimeGenerated превышает либо равно определенной дате. Но иногда необходимо узнать, существует ли в списке то или иное поле. В таких случаях применяется оператор сравнения IN. Например, оператор

WHERE EventID IN (529, 530, 531,

532, 533, 534, 535, 537, 539)

возвращает все события неудачной регистрации, приведенные в скобках. Вставив NOT перед IN, можно обратить логику и получить записи, в которых нет перечисленных идентификаторов.

Однако у оператора IN есть еще одно важное применение. Можно составить оператор WHERE, в котором LogParser использует значения одного или нескольких полей текущей строки для создания подчиненных запросов, результаты выполнения которых помогут определить, следует ли добавить текущую строку в выходные данные. Для этого можно встроить подзапрос в скобки, следующие за IN. Например, можно получить список всех пользователей, чьи учетные записи были блокированы и у кого впоследствии были сброшены пароли. Для этого нужно найти каждое событие блокирования учетной записи (ID 644), затем определить, был ли для этой записи сброшен пароль после даты и времени события блокирования. Для выполнения этих задач следует сначала составить простой запрос:

«SELECT SID AS username,
TimeGenerated AS LockoutTime
FROM security WHERE EventID =
644»

В результате будут получены имена пользователей всех когда-либо блокированных учетных записей. Затем нужно добавить следующее выражение IN:

AND username IN (SELECT SID AS
subUserName FROM security
WHERE EventID = 642 AND
subUserName = username AND
TimeGenerated > LockoutTime)

Добавив параметр -resolve SIDs, чтобы получить читаемые имена пользователей, и применив параметр AS для переименования папок, получаем команду:

logparser -i:EVT -resolveSIDs ON
«SELECT SID AS username,
TimeGenerated AS LockoutTime
FROM security WHERE EventID =
644 AND username IN (SELECT
SID AS subUserName FROM
security WHERE EventID = 642
AND subUserName = username
AND TimeGenerated >
LockoutTime)»

При запуске этой команды LogParser отыскивает все события с ID 644, затем находит все события с ID 642 с таким же именем пользователя, произошедшие после блокирования пользователя. Оператор SELECT в подзапросе должен возвращать лишь одно поле, в противном случае программе LogParser будет неизвестно, с каким полем из набора результатов подзапроса нужно проводить сравнение. В данном примере LogParser проверяет содержимое поля username в записях, полученных в результате работы подзапроса. Параметр AS мы использовали для переименования полей в обоих операторах SELECT, чтобы избежать двусмысленности при сравнении поля имени пользователя (username) в родительском запросе с полем имени пользователя (subUserName) в подчиненном запросе.

Таким образом, LogParser представляет собой мощный инструмент, с помощью которого можно составлять SQL-подобные запросы для поиска в журналах любого типа. Это позволяет находить нужную информацию, не просматривая тысячи записей журнала. Наряду с описанными в статье функциями LogParser располагает и другими возможностями для манипулирования строками, датами и числовыми полями. С помощью одного запроса можно проверить несколько журналов и даже выдать результаты в различные файлы. Кроме того, LogParser поддерживает функции агрегирования, которые позволяют выполнять высокоуровневый анализ с учетом временных интервалов, средних значений, минимумов и максимумов и 10 наиболее часто встречающихся записей для данного критерия. В одной из следующих статей я расскажу о том, как использовать такие функции для анализа данных и кодов в поле Strings в описании событий системы безопасности.

Рэнди Франклин Смит — редактор Windows & .NEtT Magazine и ведущий инструктор и разработчик курсов для программы по безопасности Windows NT/2000 института MIS Training. Его компания, Monterey Technology Group, занимается консалтингом в области информационной безопасности. Связаться с ним можно по адресу: rsmith@montereytechgroup.com


Поля журналов событий

Имена полей в журналах событий (.evt) Windows 2000 и более поздних версий следующие: EventLog, RecordNumber, TimeGenerated, TimeWritten, EventID, EventType, EventTypeName, SourceName, EventCategory, Strings, Message, ComputerName и SID. Каким образом используются эти поля, когда LogParser обрабатывает события журнала Security?

Экран A. Поля Event Viewer

EventLog содержит имя журнала событий, из которого взята текущая строка выходной таблицы LogParser. EventLog нужен только в том случае, если в операторе FROM указано несколько журналов. RecordNumber указывает номер записи события в журнале. TimeGenerated и TimeWritten показывают дату и время, когда сообщение о событии поступило в службу Event Logging, и дату и время, когда служба записала событие в журнал. Обычно значения этих полей одинаковы.

Таблица 1. Операторы сравнения LogParser
ОператорНазначение
=Равен
<Меньше, чем
>Больше, чем
<=Меньше или равно
>=Больше или равно
LIKE, NOT LIKEСравнения с универсальными символами
IN, NOT INПоиск значения в списке значений или подчиненном запросе
IS NULL, IS NOT NULLИдентифицирует поля с нулевым значением

Поле EventID содержит ID события в Windows. Например, Windows идентифицирует события блокирования учетных записей как ID 644, успешные попытки регистрации как ID 528. EventType и EventTypeName указывают, было ли событие успешным. Например, событие с ID 529 (неудачная попытка регистрации: неправильное имя пользователя или пароль) имеет EventType 16, а EventTypeName — Failure Audit event; тогда как событие с ID 528 (успешная регистрация) имеет EventType 8, а EventTypeName — Success Audit event. Поле EventType журнала Security может иметь значение 8 или 16; в журналах System и Application используются другие значения для информационных событий, предупреждений и ошибок.

Экран B. Описание события с ID 576

SourceName и EventCategory соответствуют полям Source и Category в Event Viewer (экран A). Поле SourceName не особенно полезно, так как все события имеют одинаковый источник: Security. Но EventCategory может пригодиться для сортировки и фильтрации событий в соответствии с категориями политик аудита Windows. В Windows 2000 и более поздних версиях имеется девять категорий политик аудита, которые можно увидеть, открыв интерфейс Administrative Tools, Local Security Policy и перейдя к Security SettingsLocal PoliciesAudit Policy.

EventCategory — числовое значение; в табл. A показаны значения EventCategory и соответствующие имена категорий (как имя категории политики аудита, так и имя в поле Event Viewer Category).

Поля Strings и Message относятся к многострочному тексту в разделе Description записи события. Для совместимости с различными языками служба Windows Event Log настроена так, что разработчики могут отдельно создавать статические и динамические элементы описания. Например, в разделе Description записи с ID 576 (экран B) имена полей User Name и Domain представляют собой статические элементы, а значения этих полей (LOCAL SERVICE и NT AUTHORITY) — динамические элементы, которые изменяются от одного экземпляра события к другому. Динамические элементы называются строками (string). Поэтому поле Strings в LogParser содержит все динамические элементы текущего события. Это поле используется, если нужно отфильтровать события по динамическим элементам для заданного ID — например, чтобы увидеть все экземпляры события с ID 528 (успешная регистрация) с Logon Type 2 (интерактивная). В следующих статьях будет показано, как извлекать определенные элементы из таких текстовых полей, как Strings, с помощью функций LogParser для манипулирования строками. Поле Message содержит полное описание события, т. е. и статические, и динамические элементы.

ComputerName указывает компьютер, на котором зарегистрировано событие. При анализе одного журнала это поле для всех событий одинаково. Но если одна команда LogParser используется для запроса к нескольким журналам, то поле ComputerName необходимо для идентификации компьютеров, генерировавших каждое событие.

Поле SID содержит SID учетных записей пользователей, связанных с событием. Для большинства событий SID соответствует учетной записи, вызвавшей событие, но некоторые события генерируются системными учетными записями, а не пользователями. В табл. B перечислены SID и учетные записи, которые генерировали эти события. Одна из функций LogParser преобразует SID в имена учетных записей пользователей, как описано в основной части статьи.