Windows event log xml query

Текстовая версия выступления на PHD12

Пару слов о проблеме

При реагировании на инциденты бывает необходимо посмотреть логи Windows машины, и аналитики подготавливают утилиты для удобной фильтрации событий в журналах evtx. Это связано с тем, что способы фильтрации, предложенные Microsoft, выглядят крайне не удобно.

Live response — это область, которая занимается сбором информации с работающего компьютера, чтобы определить, произошел ли инцидент.

При проведении live response анализа, полезно быстро понять, что происходило с компьютером в последнее время.

Существуют различные инструменты для проведения live response: коммерческие, с открытым исходным кодом и встроенные возможности ОС.

Использование opensource и коммерческих инструментов связано с рядом проблем:

  • Утилиты могут отправлять телеметрию разработчику

  • Отсутствие описания анализируемых журналов и EventId

Неудобство фильтрации оснасткой eventvwr.msc

Фильтрация evtx с помощью стандартной оснастки просмотра событий (eventvwr.msc) ограничена в возможностях.

Проблемы:

  • отсутствует возможность вывести информацию в колонках

  • отсутствуют регулярные выражения и поиск подстрок

  • отсутствует группировка

eventvwr.msc

eventvwr.msc

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

Ограничения XPath для фильтрации событий

Ограничения XPath для фильтрации событий

Одним из основных ограничений XPath 1.0 является то, что он не поддерживает поиск по атрибутам. То есть, если вы хотите найти события, связанные с определенным EventID, вы можете воспользоваться элементом EventID, но вы не сможете использовать атрибуты, вложенных элементов для поиска событий, например: TargetUserName.

Пример события в формате XML

Пример события в формате XML

XPath, в оснастке, не поддерживает регулярные выражения и поиск подстрок, которые могут быть полезны при фильтрации журналов событий Windows. Регулярные выражения позволяют искать текстовые строки, соответствующие определенному шаблону, что может быть полезно, например, при поиске IP-адресов или имен файлов.

Таким образом, недостатки XPath для фильтрации журналов событий Windows заключаются в ограниченных возможностях поиска по атрибутам вложенных элементов, отсутствии некоторых функций сравнения, а также отсутствии поддержки регулярных выражений.

Как показано выше, узлы «Элемент» могут содержать «Атрибуты», и мы можем использовать подстановочный знак «@» для поиска узлов «Data».

Пример ниже позволяет найти все события из журнала Security c EventID = 4688. Атрибут Path в директиве Query можно опустить. Путь к журналу указывается в в атрибуте Path директивы Select.

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[EventID=4688]]</Select>
  </Query>
</QueryList>

В пример ниже добавляем использования логического оператора OR для поиска события с EventID 4688 или 4624.

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[EventID=4688 or EventID=4624]]</Select>
  </Query>
</QueryList>

В следующем примере добавим фильтрацию по разделу XML — EventData. Для объединения двух условий в одном Select используется логический оператор AND. Вторая часть запроса начинается со знака *, который означает что верхний узел может принимать любое значение, далее мы указываем путь к атрибуту значение которого хотим указать в условии EventData -> Data -> @Атрибут=»значение».

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[EventID=4688]] and 
             *[EventData[Data[@Name="SubjectUserName"]="hacker"]]</Select>
  </Query>
</QueryList>

XPath позволяет искать значение по всем атрибутам, как в примере ниже. Структура указанные выше изменилась до EventData -> Data -> @Атрибут =»значение». Следующим запросам мы найдем все события где в атрибутах фигурировало «C:\Windows\System32\lsass.exe»

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[EventID=4688]] and 
*[EventData[Data="C:\Windows\System32\lsass.exe"]]</Select>
  </Query>
</QueryList>

Оператор Suppress позволяет исключить из конечной выборки события, которые подходят под условие в нем. В примере ниже, мы найдем все события 4624, в которых LogonType=5.

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[EventID=4624]]</Select>
    <Suppress Path="Security">*[EventData[Data[@Name='LogonType']=5]]</Suppress>
  </Query>
</QueryList>

В пределах одного Query мы можем указывать несколько Select-запросов, также есть возможность запрашивать события из разных журналов.

<QueryList>
  <Query Id="0">
    <Select Path="Security">*[System[EventID=4688]]</Select>
    <Select Path="Windows PowerShell">*</Select>
  </Query>
</QueryList>

В одном QueryList может быть несколько Query. Это удобно для логического разбиения запросов и их фильтрации.

<QueryList>
    <Query Id="0" Path="Security">
        <Select Path="Security">*[System[EventID=4688]]</Select>
    </Query>
    <Query Id="1" Path="Microsoft-Windows-Sysmon/Operational">
        <Select Path="Microsoft-Windows-Sysmon/Operational">*</Select>
    </Query>
</QueryList>

Таким образом, фильтрация evtx с помощью стандартной оснастки просмотра событий и XPath 1.0 ограничена и не всегда удобна. Для более гибкой и удобной работы с журналами событий можно использовать специализированные инструменты для анализа журналов, которые предоставляют более широкие возможности фильтрации и поиска информации.

Возможности CMD

Возможности CMD ограничены использованием двух основных утилит wevtutil и Findstr. wevtutil позволяет работать с параметрами журналов и создавать к ним запросы. Для запросов в wevtutil также используются XPath-запросы.

wevtutil.exe qe Security /q:"*[EventData[Data[@Name='TargetUserName']='User1' and Data[@Name='LogonType']=2]]" /f:text

При использовании findstr, наши возможности расширяются для поиска интересующих нас строк.

wevtutil.exe qe Security /q:"*[EventData[Data[@Name='LogonType']=11]]" /f:text | findstr "Account Name"

Возможности PowerShell

В Powershell для работы с журналами существуют два командлета Get-EventLog и Get-WinEvent. Мы рассмотри второй командлет так как он считается актуальным.

Основные возможности Get-WinEvent это использование хеш-таблиц и Xpath для фильтрации событий. Для использование хеш-таблиц используется аргумент -FilterHashtable, его можно опустить и строить запрос сразу с @{<выражение>}.

Ограничение при создании FilterHastale

Ограничение при создании FilterHastale

Рассмотрим несколько примеров использования командлета Get-WinEvent и построения конвейеров с ним.

Следующим примером мы выведем 1000 событий из журнала Security.

Get-WinEvent -LogName Security -MaxEvents 1000

Тот же запрос, но используем Format-List для приведения вывода в читаемый.

Get-WinEvent -LogName Secutity -MaxEvents 10 | Format-List

Используем FilterHashtable. Выведем события из журнала Security с EventId 4688.

Get-WinEvent @{logname="security";ID=4688} -MaxEvents 100 | Format-List

Для понимания дальнейших фильтров давайте посмотрим как представлено событие в Powershell, для этого выведем одно событие и воспользуемся командлетом Get-Member.

Get-WinEvent @{logname="security";ID=4688} -MaxEvents 1 | Get-Member

Свойство Properties — это список, который хранит основные параметры события, которые расположены в секции EventData xml-представления.

После того, как мы научились получать события из определенного журнала и по определенному EventId, давайте посмотрим каким образом мы можем получить интересующие нас данные. Давайте выведем события 4688, и отобразим время создания события и {$_.Properties[5].value}, которое хранит имя запущенного процесса. Номер элемента списка Properties соответствует номеру атрибута в секции EventData xml-представления события.

Get-WinEvent @{logname="security";ID=4688} -MaxEvents 100 | 
select timecreated,{$_.Properties[5].value} | Format-List

Powershell позволяет нам создавать группировки, используя командлет Group-Object. Следующим запросом сгруппируем события Sysmon EventId 3 по следующим полям: процесс, адрес получателя, доменное имя получателя, порт получателя.

Get-WinEvent @{LogName="*sysmon*";ID=3}  -MaxEvents 10000 | 
Where-Object {$_.Properties[16].Value -ne 443} | 
Group-Object {$_.Properties[4].Value},{$_.Properties[14].Value}, {$_.Properties[15].Value}, {$_.Properties[16].Value} | 
Select-Object Name, Count | Sort-Object Count -Descending  | Format-List

Для поиска подстрок можем воспользоваться -Match или -Like.

Get-WinEvent @{LogName="*sysmon*";ID=1}  -MaxEvents 1000 | 
Where-Object {$_.Properties[10].Value -Match ".*cmd.exe" }  | 
Select-Object {$_.Properties[10].Value} | 
Format-List

Поиска событий с известным значением атрибута выполняется быстрее при использовании XPath запросов, чем конвейеров Powershell. Например: запрос ниже найдет все события, связанные с пользователем R00t1k\Vadim.

Get-WinEvent -LogName *Sysmon* -FilterXPath "*[EventData[Data[@Name='User']='LAB\vadim']]" | 
Where-Object {$_.Properties[4].Value -Match ".*WINWORD.exe"} | 
Format-List

Poweshell позволяет представить сообщение в виде xml, выполнив следующий код.

$eventlog = Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624} -MaxEvents 1
$xml = [xml]$eventlog.ToXml()
$xml.Event.EventData.Data

Модуль Powershell Convert-EventLogRecord преобразовывает события в структуру данных, которая позволяет обращаться к атрибутам по их имени. Конвейер для фильтрации событий с Convert-EventLogRecord будет выполняться в 2-3 раза дольше, в отличие от ковейера без его использования.

# С использованием Convert-EventLogRecord
Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624} | 
Convert-EventLogRecord | Where-Object LogonType -ne 5 | 
Select TimeCreated, TargetUserName, LogonType

# Без использования Convert-EventLogRecord
Get-WinEvent @{LogName="Security";Id=4624} | 
Where-Object {$_.Properties[8].Value -ne 5} | 
Select TimeCreated, {$_.Properties[5].Value}, {$_.Properties[8].Value}

Используя командлет Out-GridView мы можем вывести события в виде таблицы.

Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624} |
    Select-Object TimeCreated, 
@{Name='User'; Expression={$_.Properties[5].Value}}, 
@{Name='LogonType'; Expression={$_.Properties[8].Value}},
@{Name='SrcIp'; Expression={$_.Properties[18].Value}}  |
    Out-GridView

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

 Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624, 4688} -MaxEvents 1000 | Where-Object {$_.Properties[8].Value -ne 5} |
    ForEach-Object {
        if ($_.Id -eq 4624) {
            $Username = $_.Properties[5].Value
            $LogonType = $_.Properties[8].Value
            $LogonProcess = $_.Properties[9].Value
            $IpAddress = $_.Properties[18].Value
        }
        elseif ($_.Id -eq 4688) {
            $SubjectUserName = $_.Properties[1].Value
            $SubjectUserDomain = $_.Properties[2].Value
            $NewProcessName = $_.Properties[5].Value
            $CommandLine= $_.Properties[8].Value
            $ParrentProcessName= $_.Properties[13].Value
        }
        [PSCustomObject]@{
            Time = $_.TimeCreated
            EventID = $_.Id
            Username = $Username
            LogonType = $LogonType
            LogonProcess = $LogonProcess
            IpAddress = $IpAddress
            SubjectUserName = $SubjectUserName
            SubjectUserDomain = $SubjectUserDomain
            NewProcessName = $NewProcessName
            CommandLine = $CommandLine
            ParrentProcessName = $ParrentProcessName
                    }
    } | Out-GridView

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

Подписывайтесь на Telegram Detection is easy

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

Какими утилитами при реагировании на инциденты Вы пользуетесь?

Проголосовали 25 пользователей. Воздержались 10 пользователей.

Журнал событий Windows (Event Log) — это важный инструмент, который позволяет администратору отслеживать ошибки, предупреждения и другие информационные сообщения, которые регистрируются операционной системой, ее компонентами и различными программами. Для просмотра журнала событий Windows можно использовать графическую MMC оснастку Event Viewer (
eventvwr.msc
). В некоторых случаях для поиска информации в журналах событий и их анализа гораздо удобнее использовать PowerShell. В этой статье мы покажем, как получать информацию из журналов событий Windows с помощью командлета Get-WinEvent.

Содержание:

  • Получение логов Windows с помощью Get-WinEvent
  • Get-WinEvent: быстрый поиск в событиях Event Viewer с помощью FilterHashtable
  • Расширенный фильтры событий Get-WinEvent с помощью FilterXml
  • Получить логи Event Viewer с удаленных компьютеров

На данный момент в Windows доступны два командлета для доступа к событиям в Event Log: Get-EventLog и Get-WinEvent. В подавляющем большинстве случаев рекомендуем использовать именно Get-WinEvent, т.к. он более производителен, особенно в сценариях обработки большого количества событий с удаленных компьютеров. Командлет Get-EventLog является устаревшим и использовался для получения логов в более ранних версиях Windows. Кроме того, Get-EventLog не поддерживается в современных версиях PowerShell Core 7.x.

Получение логов Windows с помощью Get-WinEvent

Для использования команды Get-WinEvent нужно запустить PowerShell с правами администратора (при запуске Get-WinEvent от имени пользователя вы не сможете получить доступ к некоторым логам, например, к Security).

Для получения списка событий из определенного журнала, нужно указать его имя. В данном примере мы выведем последние 20 событий из журнала System:

Get-WinEvent -LogName Application -MaxEvents 20

Чаще всего вам нужно будет получать информацию из журналов System, Application, Security или Setup. Но вы можете указать и другие журналы. Полный список журналов событий в Windows можно получить с помощью команды:

Get-WinEvent -ListLog *

Get-WinEvent командлет PowerShell

Например, чтобы вывести события RDP подключений к компьютеру, нужно указать лог Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational:

Get-WinEvent -LogName Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational

Или получить логи SSH подключений к Windows из журнала OpenSSH/Operational:

Get-WinEvent -LogName OpenSSH/Operational

Можно выбрать события сразу из нескольких журналов. Например, чтобы получить информацию о ошибках и предупреждениях из журналов System и Application за последние 24 часа (сутки), можно использовать такой код:

$StartDate = (Get-Date) - (New-TimeSpan -Day 1)
Get-WinEvent Application,System | Where-Object {($_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning") -and ($_.TimeCreated -ge $StartDate )}

Get-WinEvent командлет для поиска событий в журнале Windows

Чтобы вывести только определенные поля событий, можно использовать Select-Object или Format-Table:

Get-WinEvent -LogName System | Format-Table Machinename, TimeCreated, Id, UserID

Get-WinEvent вывести определенные поля событий

Можно выполнить дополнительные преобразования с полученными данными. Например, в этом примере мы сразу преобразуем имя пользователя в SID:

Get-WinEvent -filterhash @{Logname = 'system'} |
Select-Object @{Name="Computername";Expression = {$_.machinename}},@{Name="UserName";Expression = {$_.UserId.translate([System.Security.Principal.NTAccount]).value}}, TimeCreated

Get-WinEvent: быстрый поиск в событиях Event Viewer с помощью FilterHashtable

Рассмотренный выше способ выбора определенных событий из журналов Event Viewer с помощью Select-Object прост для понимая, но выполняется крайне медленно. Это особенно заметно при выборке большого количества событий. В большинстве случаев для выборки событий нужно использовать фильтрацию на стороне службы Event Viewer с помощью параметра FilterHashtable.

Попробуем сформировать список ошибок и предупреждений за 30 дней с помощью Where-Object и FilterHashtable. Сравнима скорость выполнения этих двух команд PowerShell с помощью Measure-Command:

$StartDate = (Get-Date).AddDays(-30)

Проверим скорость выполнения команды с Where-Object:

(Measure-Command {Get-WinEvent Application,System | Where-Object {($_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning") -and ($_.TimeCreated -ge $StartDate )}}).TotalMilliseconds

Аналогичная команда с FilterHashtable:

(Measure-Command {Get-WinEvent -FilterHashtable @{LogName = 'System','Application'; Level =2,3; StartTime=$StartDate }})..TotalMilliseconds

В данном примере видно, что команда выборки событий через FilterHashtable выполняется в 30 раз быстрее, чем если бы обычный Where-Object (
2.5
сек vs
76
секунд).

Get-WinEvent FilterHashtable выполняется намного быстрее

Если вам нужно найти события по EventID, используйте следующую команду с FilterHashtable:

Get-WinEvent -FilterHashtable @{logname='System';id=1074}|ft TimeCreated,Id,Message

В параметре FilterHashtable можно использовать фильтры по следующим атрибутам событий:

  • LogName
  • ProviderName
  • Path
  • Keywords (для поиска успешных событий нужно использовать значение 9007199254740992 или для неуспешных попыток 4503599627370496)
  • ID
  • Level (1=FATAL, 2=ERROR, 3=Warning, 4=Information, 5=DEBUG, 6=TRACE, 0=Info)
  • StartTime
  • EndTime
  • UserID (SID пользователя)
  • Data

Пример поиска события за определенный промежуток времени:

Get-WinEvent -FilterHashTable @{LogName='System'; StartTime=(get-date).AddDays(-7); EndTime=(get-date).AddHours(-1); ID=1234}

Если нужно найти определенный текст в описании события, можно использовать такую команду:

Get-WinEvent -FilterHashtable @{logname='System'}|Where {$_.Message -like "*USB*"}

Get-WinEvent поиск текста в событиях

Расширенный фильтры событий Get-WinEvent с помощью FilterXml

Фильтры Get-WinEvent с параметром FilterHashtable являются несколько ограниченными. Если вам нужно использовать для выборки событий сложные запросы с множеством условий, нужно использовать параметр FilterXml, который позволяет сформировать запрос на выбор событий в Event Viewer с помощью XML запроса. Как и FilterHashtable, фильтры FilterXml выполняется на стороне сервера, поэтому результат вы получите довольно быстро.

Например, аналогичный запрос для получения последних ошибок из журнала System за последние 30 дней может выглядеть так:

$xmlQuery = @'
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[(Level=2 or Level=3) and TimeCreated[timediff(@SystemTime) &lt;= 2592000000]]]</Select>
</Query>
</QueryList>
'@
Get-WinEvent -FilterXML $xmlQuery

Get-WinEvent -FilterXML

Для построения кода сложных XML запросов можно использовать графическую консоль Event Viewer:

  1. Запустите
    eventvwr.msc
    ;
  2. Найдите журнал для которого вы хотите создать и выберите Filter Current Log;
  3. Выберите необходимые параметры запроса в форме. В этом примере я хочу найти события с определенными EventID за последние 7 дней от определенного пользователя;
  4. Чтобы получить код XML запроса для параметра FilterXML, перейдите на вкладку XML и скопируйте полученный код (CTRL+A, CTRL+C);
    XML запрос в Event Viewer

  5. Если нужно, вы можете вручную отредактировать данный запрос.

Для экспорта списка событий в CSV файл нужно использовать командлет Export-CSV:

$Events= Get-WinEvent -FilterXML $xmlQuery
$events| Export-CSV "C:\ps\FilterSYSEvents.csv" -NoTypeInformation -Encoding UTF8

Получить логи Event Viewer с удаленных компьютеров

Для получения события с удаленного компьютер достаточно указать его имя в параметре -ComputerName:

$computer='msk-dc01'
Get-WinEvent -ComputerName $computer -FilterHashtable @{LogName="System"; StartTime=(get-date).AddHours(-24)} |   select Message,Id,TimeCreated

Можно опросить сразу несколько серверов/компьютеров и поискать на них определенные события. Список серверов можно получить из текстового файла:

$servers = Get-Content -Path C:\ps\servers.txt

Или из Active Directory:

$servers = (Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"').Name
foreach ($server in $servers) {
Get-WinEvent -ComputerName $server -MaxEvents 5 -FilterHashtable @{
LogName = 'System'; ID= 1234
} | Select-Object -Property ID, MachineName
}

Здесь есть другой пример для поиска событий блокировки учетной записи пользователя на всех контроллерах домена:

$Username = 'a.ivanov'
Get-ADDomainController -fi * | select -exp hostname | % {
$GweParams = @{
‘Computername’ = $_
‘LogName’ = ‘Security’
‘FilterXPath’ = "*[System[EventID=4740] and EventData[Data[@Name='TargetUserName']='$Username']]"
}
$Events = Get-WinEvent @GweParams
$Events | foreach {$_.Computer + " " +$_.Properties[1].value + ' ' + $_.TimeCreated}
}

If you are looking to filter certain events in the Event Log the traditional approach is to use the filter option with the right click «Filter current event log» as below which should be familiar to anyone working with event logs:

However you also get the XML tab which at the moment is greyed out as you can see below:

If you then tick the box «Edit query manually» you will get a warning dialogue that tells you that you cannot use the «modify controls» from earlier, you need to accept this with a Yes:

Then you have the option to use XML to query the Event Log, which means you can be way more granular than the graphical version with your commands.

XML : View Modification to Active Directory by a certain Employee

<QueryList>

  <Query Id=»0″ Path=»Security»>

    <Select Path=»Security»>

      *[System[(EventID=5136)]]

      and

      *[EventData[Data[@Name=’SubjectUserName’] and (Data=’perpetrator‘)]]

    </Select>

  </Query>

</QueryList>

XML : View Modifications to Active Directory Group Policy by a certain employee

<QueryList>

  <Query Id=»0″ Path=»Security»>

    <Select Path=»Security»>

      *[System[(EventID=5136 or EventID=5137 or EventID=5141)]]

      and

      *[EventData[Data[@Name=’ObjectClass’] and (Data=’groupPolicyContainer’)]]

      and

      *[EventData[Data[@Name=’SubjectUserName’] and (Data=’perpetrator‘)]]

    </Select>

  </Query>

</QueryList>

XML : View any modifications on that server with Auditing enabled

<QueryList>

  <Query Id=»0″ Path=»Security»>

    <Select Path=»Security»>

      *[EventData[Data[@Name=’SubjectUserName’] and (Data=’perpetrator‘)]]

    </Select>

  </Query>

</QueryList>

This will give you a flavour of what can be done, however what if you need to script this to apply to all your domain controllers and then output a CSV at the end of it with all that data?

Create a file called GPOUpdateQuery.xml and add in that file place the XML you would like to run on the target servers in this case its for GPO modifications, then we need that XML file to run the script to export all the entries you require.

Script : GPOMonitorXML.ps1

$xmlQuery | Out-File -FilePath «GPOUpdateQuery.xml»

# Get all domain controllers

$domainControllers = Get-ADDomainController -Filter * | Select-Object -ExpandProperty Hostname

# Initialize an array to store results

$results = @()

# Query each domain controller

foreach ($dc in $domainControllers) {

    Write-Host «Querying $dc…»

    $events = Get-WinEvent -ComputerName $dc -FilterXml (Get-Content «GPOUpdateQuery.xml»)

    foreach ($event in $events) {

        $eventData = @{

            TimeCreated = $event.TimeCreated

            DomainController = $dc

            EventID = $event.Id

            UserName = $event.Properties[4].Value

            GPOName = $event.Properties[5].Value

            Action = switch ($event.Id) {

                5136 { «Modified» }

                5137 { «Created» }

                5141 { «Deleted» }

            }

        }

        $results += New-Object PSObject -Property $eventData

    }

}

# Export results to CSV

$results | Export-Csv -Path «GPOUpdates.csv» -NoTypeInformation

Write-Host «Query complete. Results exported to GPOUpdates.csv»

Windows Event viewer/Event Log is one of the important tools for system administrators to troubleshoot system-related issues and track suspicious activities. You can simply open the Event viewer and check the logs, but sometimes we need to use PowerShell to efficiently query and filter the events.

We can use the Get-WinEvent cmdlet to get events from event logs and event trace log files on local and remote computers. Before you start, open the PowerShell with “Run as administrator” permission since some of the logs (ex: Security log) requires elevated privilege.

Summary

  • List all logs – Log names and configuration
  • Get all events from a specific event Log
  • Use Basic Filter to Query and Search Event Log
  • Filter events on the server-side using the FilterHashtable parameter
  • Use Advanced Filter to Query and Search Event Log
  • Export event logs to a CSV file

List all logs – Log names and configuration

Events are logged under different log categories such as Application, System, etc. The Get-WinEvent cmdlet uses the ListLog parameter to retrieve the specific log information, we can provide the asterisk (*) wildcard to this parameter to list all the available logs. The below command gets the available logs on the local computer.

If you’re not running PowerShell as an Administrator, you will get the below error message for the logs which require Run as administrator privilege.

Get-WinEvent : Could not retrieve information about the Security log. Error: Attempted to perform an unauthorized operation.

Get all events from a specific event Log

The Get-WinEvent cmdlet uses the LogName parameter to specify the event logs (ex: Application, Security) that this cmdlet gets events from. We can provide a single event log name or enter the log names in a comma-separated list. Wildcards are also permitted (ex: Get-WinEvent -LogName *PowerShell*).

The below command retrieves the latest 100 events from the Application log.

Get-WinEvent -LogName 'Application' -MaxEvents 100

Run the below command to get the events from the Security log.

Get-WinEvent -LogName 'Security' -MaxEvents 100

The below command gets the events from the Windows PowerShell and Setup logs. The log names are provided as comma-separated values. The command returns the number of events that are grouped by the Level such as Error or Warning and the log name.

Get-WinEvent -LogName 'Windows PowerShell', 'Setup' |
  Group-Object -Property LevelDisplayName, LogName -NoElement | Format-Table -AutoSize

Use Basic Filter to Query and Search Event Log

The Get-WinEvent cmdlet retrieves all events from the given Event log. In some cases, this will lead to thousands of event messages, and we can’t be able to easily locate the required event. We need to apply the required filter while querying the events.

We can use the Where-Object cmdlet to filter the retrieved events. This will provide the required result, but the filters are applied after the objects are retrieved from the log. In other words, this is a client-side filter, and we may face performance degradation if the log contains a large number of events.

#Get events for last 30 days from Setup log
$StartDate = (Get-Date) - (New-TimeSpan -Day 30) 
Get-WinEvent -LogName 'Setup' | Where-Object { $_.TimeCreated -ge $StartDate }

For better performance, we can use the server-side filters supported by the Get-WinEvent cmdlet, such as FilterHashtable (Basic) and FilterXML (Advanced).

Filter events on the server-side using the FilterHashtable parameter

The FilterHashtable parameter specifies a query in hash table format to select events from one or more event logs. The below command fetches the events for the last 2 days from the Application log.

$StartDate = (Get-Date).AddDays(-2)
Get-WinEvent -FilterHashtable @{ LogName='Application'; StartTime=$StartDate;}

We can also filter the events by an Event ID value. Run the following command to retrieve the logon events (4624) from the Security log.

$StartDate = (Get-Date).AddDays(-2)	
Get-WinEvent -FilterHashtable @{ LogName='Security'; StartTime=$StartDate; Id='4624'}

This command uses the FilterHashtable parameter to find events generated in the last 10 days by Windows Installer (‘MsiInstaller’).

$StartDate = (Get-Date).AddDays(-10)	
Get-WinEvent -FilterHashtable @{ Logname='Application';ProviderName='MsiInstaller';StartTime=$StartDate }

For more information about the FilterHashtable parameter, see How to create Get-WinEvent queries with FilterHashtable.

Use Advanced Filter to Query and Search Event Log

You can create a basic level filter using the FilterHashtable parameter, but we need to use the FilterXml parameter for complex queries with multiple conditions. The FilterXml parameter can be used to search events on the server side by using a structured XML query.

The below command retrieves the Critical, Error, and Warning level events for the last 30 days from the Application log.

#Using the FilterXML parameter:
$xmlQuery = @'
<QueryList>
  <Query Id="0" Path="Application">
    <Select Path="Application">*[System[(Level=1  or Level=2 or Level=3) and TimeCreated[timediff(@SystemTime) &lt;= 2592000000]]]</Select>
  </Query>
</QueryList>
'@
Get-WinEvent -FilterXML $xmlQuery

The tricky part with this method is creating the XML query for your requirement. To create a valid XML query, use the Create Custom View and Filter Current Log features in Windows Event Viewer.

Follow the below steps to generate a valid XML query using Windows Event Viewer.

  • Open the Event Viewer (open the Run window, type eventvwr.msc, and press the ENTER key).
  • On the left-hand side, right-click on Custom Views and select Create Custom View option.
  • Select time interval (Logged – Last 7 days) and select the required Event levels to filter such as Critical, Error, and Warning.
  • Choose the By Log option and select System under Windows Logs. Add more filter options if needed. 
  • Finally, click the XML tab to view the query in XML format. You can copy the XML query from the XML tab (Ctrl+A and Ctrl+C) and use the value in the FilterXml parameter.

This command gets the events for the last 7 days from the System log. The XML query filters the events with the event levels Critical, Error, and Warning.

#Using the FilterXML parameter:
$xmlQuery = @'
<QueryList>
  <Query Id="0" Path="System">
    <Select Path="System">*[System[(Level=1  or Level=2 or Level=3) and TimeCreated[timediff(@SystemTime) &lt;= 604800000]]]</Select>
  </Query>
</QueryList>
'@
Get-WinEvent -FilterXML $xmlQuery 

Export event logs to a CSV file

Once we retrieved the required events, we can export the log result to a CSV file using the Export-CSV cmdlet. The following command queries the events (exclude the Level 4 events – Information) from the System log for the last 30 days and exports the result to a CSV file.

$xmlQuery = @'
<QueryList>
  <Query Id="0" Path="System">
    <Select Path="System">*[System[(Level=1  or Level=2 or Level=3 or Level=5) and TimeCreated[timediff(@SystemTime) &lt;= 2592000000]]]</Select>
  </Query>
</QueryList>
'@

#Get events with FilterXML
$Events= Get-WinEvent -FilterXML $xmlQuery 

#Export the System events to the CSV file
$Events | Export-CSV "C:\Temp\SystemEvents.CSV" -NoTypeInformation -Encoding UTF8

The following command exports the Critical, Error, and Warning level events for the last 30 days from the Application log.

$xmlQuery = @'
<QueryList>
  <Query Id="0" Path="Application">
    <Select Path="Application">*[System[(Level=1  or Level=2 or Level=3) and TimeCreated[timediff(@SystemTime) &lt;= 2592000000]]]</Select>
  </Query>
</QueryList>
'@

#Get events with FilterXML
$Events= Get-WinEvent -FilterXML $xmlQuery 

#Export the Application events to the CSV file
$Events | Export-CSV "C:\Temp\ApplicationEvents.CSV" -NoTypeInformation -Encoding UTF8

When working with Windows event logs, especially the Security log, there might be instances where you need to extract specific information from events.

But you’ll find out, it’s not as easy as you’d anticipate.

Recently, I needed to query Windows events from the Security event log for user logon events (Event ID 4624 to be specific). But not all events, just events matching a specific username syntax of domain_name/username.

Join me on a journey of Windows event logs, XML and XPath as we parse Windows event logs with PowerShell. To demonstrate, let’s walk through an example.

Query the Event Log with Get-WinEvent

You first need to pull at least one Windows event. Since I’m working with user logon events, I’ll pull just one event as an example with event ID 4624.

$eventRecord = Get-WinEvent -MaxEvents 1 -FilterHashtable @{LogName='Security';ID=4624}

Done! You now have an System.Diagnostics.Eventing.Reader.EventLogRecord object.

Convert the Event Record to XML

Now, this tutorial would be pretty short if I just needed to filter on basic properties like Id or TimeCreated but unfortunately, I need to filter information from an event’s Message field. Get-WinEvent doesn’t create a friendly object for us to query. Instead, you must build your own.

To do so, you must first convert the record to XML using the ToXml() method.

The ToXml() method converts the entire event object to an XML string.

$xmlEventRecord = $eventRecord.ToXml()

Unless you’re a sadomasochist and prefer to use regex, you need to get this into a structured format to query elements inside of it. Lucky for us, you can easily cast XML strings to Xml.Document types using the [xml] type accelerator.

$xmlEventRecord = [xml]$eventRecord.ToXml()

Parse the XML to Extract Domain and Username

The XML format of the event contains various elements, and we need to navigate through these to find TargetUserName and TargetDomainName. We will use XPath queries to achieve this.

💡 Windows event logs don’t have full XPath support. They use a subset of XML 1.0 that is severely limited in the functions you can use.

On every XmlDocument object, you have a SelectSingleNode() method that allows you to pass an XPath query to to find elements in the document but you need to come up with the XPath query first.

When building XPath queries, you must first figure out the “path” to the entity you’re querying or at least the node that holds the value you’re after. In this case, the elements we’re after are in EventEventData stored as a Data node with Name attributes with values of TargetUserName and TargetDomainName that we’re after.

<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
    <System>
        <Provider Name='Microsoft-Windows-Security-Auditing' Guid='{54849625-5478-4994-a5ba-3e3b0328c30d}'/>
        <EventID>4624</EventID>
        <Version>2</Version>
        <Level>0</Level>
        <Task>12544</Task>
        <Opcode>0</Opcode>
        <Keywords>0x8020000000000000</Keywords>
        <TimeCreated SystemTime='2024-06-07T17:23:16.5812423Z'/>
        <EventRecordID>260494</EventRecordID>
        <Correlation/>
        <Execution ProcessID='768' ThreadID='1708'/>
        <Channel>Security</Channel>
        <Computer>ATA-Devo-SRV-01.atadevo.local</Computer>
        <Security/>
    </System>
    <EventData>
        <Data Name='SubjectUserSid'>S-1-5-18</Data>
        <Data Name='SubjectUserName'>ATA-DEVO-SRV-01$</Data>
        <Data Name='SubjectDomainName'>ATADEVO</Data>
        <Data Name='SubjectLogonId'>0x3e7</Data>
        <Data Name='TargetUserSid'>S-1-5-21-3909178112-971454023-1868345993-500</Data>
        <Data Name='TargetUserName'>atadevoadmin</Data>
        <Data Name='TargetDomainName'>ATADEVO</Data>
        <Data Name='TargetLogonId'>0x546f80</Data>
        <Data Name='LogonType'>7</Data>
        <Data Name='LogonProcessName'>Negotiat</Data>
        <Data Name='AuthenticationPackageName'>Negotiate</Data>
        <Data Name='WorkstationName'>ATA-DEVO-SRV-01</Data>
        <Data Name='LogonGuid'> { 97292dbd-b376-1168-64b5-b750cafa6348 }</Data>
        <Data Name='TransmittedServices'>-</Data>
        <Data Name='LmPackageName'>-</Data>
        <Data Name='KeyLength'>0</Data>
        <Data Name='ProcessId'>0x300</Data>
        <Data Name='ProcessName'>C:WindowsSystem32lsass.exe</Data>
        <Data Name='IpAddress'>-</Data>
        <Data Name='IpPort'>-</Data>
        <Data Name='ImpersonationLevel'>%%1833</Data>
        <Data Name='RestrictedAdminMode'>-</Data>
        <Data Name='TargetOutboundUserName'>-</Data>
        <Data Name='TargetOutboundDomainName'>-</Data>
        <Data Name='VirtualAccount'>%%1843</Data>
        <Data Name='TargetLinkedLogonId'>0x0</Data>
        <Data Name='ElevatedToken'>%%1842</Data>
    </EventData>
</Event>

If you don’t work with XPath too often, you’re first inclination would probably be to do something like this:

$xmlEventRecord.SelectSingleNode("//Data[@Name='TargetUserName']")

The XPath query is sound. It’s looking for all Data nodes with an attribute of Name and a value of TargetUserName. But, it won’t work. Why? This XML use a namespace.

xmlns='http://schemas.microsoft.com/win/2004/08/events/event

XML namespaces are used for providing uniquely named elements and attributes in an XML document. An XML instance may contain element or attribute names from more than one XML vocabulary. If each vocabulary is given a namespace, the ambiguity between identically named elements or attributes can be resolved.

To query an XML document that uses a namespace, you must first define an XmlNamespaceManager and then add that namespace to it.

$eventNamespace = New-Object System.Xml.XmlNamespaceManager($xmlEventRecord.NameTable)
$eventNamespace.AddNamespace("evt", "http://schemas.microsoft.com/win/2004/08/events/event")

Once you have the namespace defined, you must then use that namespace in the SelectSingleNode() query.

$xmlEventRecord.SelectSingleNode("//evt:Data[@Name='TargetUserName']", $eventNamespace)

At this point, you should be able to extract the XML node.

Finding the Text Value

The SelectSingleNode() method will not return a simple string that you’ll need. Instead, it returns an System.Xml.XmlElement object which you’ll need to extract the node value from. To do that, you can use your familiar PowerShell dot notation to reference the InnerText property.

$xmlEventRecord.SelectSingleNode("//evt:Data[@Name='TargetUserName']", $eventNamespace).InnerText

Once you know how to find the text value, you’re back into familiar PowerShell territory. At this point, you just need to extract both of the strings (TargetUserName and TargetDomainName) in this case and concatenate them together.

$targetUserName = $xmlEventRecord.SelectSingleNode("//evt:Data[@Name='TargetUserName']", $eventNamespace).InnerText
$targetDomainName = $xmlEventRecord.SelectSingleNode("//evt:Data[@Name='TargetDomainName']", $eventNamespace).InnerText
$domainUser = "$targetDomainName$targetUserName"

Finally, combine the extracted TargetUserName and TargetDomainName to form the DomainNameUsername format.

And you’re done!

Conclusion and Script

If you’re building any PowerShell scripts querying Windows event logs, chances are you’re going to have to especially if you want to query the Message field.

Here’s the complete script if you want to copy and paste.

# Step 1: Query the Security log for Event ID 4624
$eventRecord = Get-WinEvent -MaxEvents 1 -FilterHashtable @{LogName='Security';ID=4624}

# Step 2: Convert the event record to XML
$xmlEventRecord = [xml]$eventRecord.ToXml()

# Step 3: Define the namespace manager
$eventNamespace = New-Object System.Xml.XmlNamespaceManager($xmlEventRecord.NameTable)
$eventNamespace.AddNamespace("evt", "<http://schemas.microsoft.com/win/2004/08/events/event>")

# Step 4: Extract TargetUserName and TargetDomainName using XPath
$targetUserName = $xmlEventRecord.SelectSingleNode("//evt:Data[@Name='TargetUserName']", $eventNamespace).InnerText
$targetDomainName = $xmlEventRecord.SelectSingleNode("//evt:Data[@Name='TargetDomainName']", $eventNamespace).InnerText

# Step 5: Combine DomainName and UserName
$domainUser = "$targetDomainName\$targetUserName"
$domainUser

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Epson l120 driver windows 10
  • Windows ctrl alt del screen
  • Цветокоррекция монитора windows 11
  • Можно ли удалять дампы памяти windows
  • American truck simulator windows 10