EzETW — Got To Catch Them Al
FalconFriday — EzETW — Got To Catch Them All…
March 11, 2022
Time flies when you’re a Falcon! It’s been more than a month since I joined the FalconForce crew, and I didn’t find any time to introduce myself. To fix this, I decided to write a blog post and share a little tool along with it…

TL;DR: This post will present the EzETW tool and go over basic Windows events PowerShell cmdlet syntax. If you just came for the tool, you can get it here.

Making the switch from offensive consulting to detection engineering, I get to put my hands on a lot of new stuff. I’m like a pre-COVID kid in a candy shop… And since I come from the world of Windows automation (aka PowerShell), I have the bad habit of creating cmdlets for everything.

In the cloud or on the host, one thing that seems to be a recurring need in the life of a detection engineer, is the need to catch events. In this post, I’ll take a quick dive into catching events on the host with PowerShell.

I. Windows events with PowerShell
Several PowerShell cmdlets can be used to query event logs:

  • the legacy Get-EventLog cmdlet from the Microsoft.PowerShell.Utility module.
  • the modern Get-WinEvent cmdlet from the Microsoft.PowerShell.Diagnostics module.

The Get-EventLog cmdlet works only on Windows classic event logs and uses a deprecated Win32 API. The results may not be accurate. So I highly recommend you use the Get-WinEvent cmdlet instead.

The Get-WinEvent cmdlet gets events from event logs generated by the (classic) Windows event log technology and by the (modern) Event Tracing for Windows (ETW) technology. Type Get-Help Get-WinEvent -full for more info.

In short, when it comes to manipulating events, Get-WinEvent is the PowerShell way to go. This single cmdlet can do by itself probably most of what you need, but the syntax is sometimes a bit tricky. Below a quick recap of the cmdlet functionalities and syntax.

a. Listing logs

To list available logs on your system, you can type the following:

# List all logs.
Get-WinEvent -ListLog *
# List all logs (ignore errors).
Get-WinEvent -ListLog * -ea 0
# List logs with content.
Get-WinEvent -ListLog * -ea 0 | Where RecordCount
# Find logs by keyword.
Get-WinEvent -ListLog * -ea 0 | Where RecordCount | Where LogName -match ‘PowerShell’

If you want to view all properties of a specific log, you can do the following:

Get-WinEvent -ListLog 'Windows PowerShell' | Select *

b. Listing providers

To list available providers on your system, you can type the following:

# List providers.
Get-WinEvent -ListProvider * -ea 0
# List providers matching security.
Get-WinEvent -ListProvider * -ea 0 | Where Name -match 'Security' | select name
# View full provider properties.
Get-WinEvent -ListProvider PowerShell | Select *
# Get provider GUID.
(Get-WinEvent -ListProvider Microsoft-Windows-Security-Auditing).Id.Guid

You can view a list of all event types for a specific provider with the following command:

# Provider event id overview
Get-WinEvent -ListProvider Microsoft-Windows-PowerShell | Select -expand events | select id,description

c. Querying logs

Now that we have seen logs and providers, let’s catch some events:

# View events from specified log.
Get-WinEvent -LogName 'Windows PowerShell' -Oldest -MaxEvents 500
# Get events / filter hashtable.
Get-WinEvent -Oldest -MaxEvents 500 -FilterHashtable @{logname='Security';id=4662}
# Get events / filter Xpath.
$xfilter = "*[System[TimeCreated[@SystemTime >= '2022–02–22T22:22:22']]]"
Get-WinEvent -oldest -maxevent 500 -LogName 'Security' -FilterXPath $xfilter

Note: the Get-WinEvent cmdlet has -ComputerName and -Credential parameters allowing you to query remote computers.

d. On-demand tracing

It is possible to use ETW sessions to capture events from several providers into an etl file. The Get-WinEvent cmdlet can then be used with the -Path switch to read events from this file.

Type Get-Command -Module EventTracingManagement for a list of ETW-related cmdlets.

Type Get-Help Start-EtwTraceSession -full for extended help on this cmdlet.

The basic steps to record events on the fly would go as follows:

# Create ETW session.
Start-EtwTraceSession -Name MyTraceSession -LocalFilePath "$PWD\NefariousTrace.etl" -RealTime
# Add provider(s) to session.
$Guid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Add-EtwTraceProvider -SessionName MyTraceSession -Guid "{$Guid}"
# Do something nefarious.
Invoke-Something -Nefarious
# Stop tracing.
Stop-EtwTraceSession -Name MyTraceSession
# Read etl output.
Get-WinEvent -Path "$PWD\NefariousTrace.etl" -Oldest

With the above commands, you can trace events from providers that are not logged by default. Many of these providers are unexplored territories, full of unknown events waiting for you to catch them. And if you want to, you could even catch them on remote systems with the -CimSession parameter…

II. Easy events with EzETW

As seen in the previous part, PowerShell gives us access to logs and events as objects. This is quite cool. But what if we could make it even easier? Less typing… More results. To reduce my daily keystroke count, and increase my efficiency as a detection engineer, I decided I needed some DIY cmdlets to wrap all this the easy way…

a. Get-EzEvent

First up is Get-EzEvent. It basically does the same things than Get-WinEvent, except you don’t have to specify the ‘*’ for ‘any’. It returns only logs with records by default, it doesn’t scream errors back at you… and other little stuffs like that. But sometimes it’s in the little stuffs…

  • List logs
# List all logs.
EzEvent -ListLog
# Find log.
EzEvent -ListLog -Match 'PowerShell'
  • List providers
# List all providers.
EzEvent -ListProvider
# Find provider.
EzEvent -ListProvider -Match 'PowerShell'

Note: the above commands to list logs and providers return simple lists by default. Adding -AsObject would return full objects.

  • List event IDs
# List event IDs for specific provider.
EzEvent -ListEvent -Provider 'Microsoft-Windows-PowerShell'
  • Get events
# Get all events [/!\ Returns a lot of stuff].
# Get events from a specific log.
EzEvent -Log 'Windows PowerShell'
# Get events XPath/Max/Regex.
EzEvent -Log 'Windows PowerShell' -FilterXPath * -Max 2000 -Match 'pipeline'

Using ‘*’ as Xpath filter is pointless of course, so what about filters?

b. Get-EzEventFilter

The most efficient way of filtering events is with Xpath filters, but these are a real pain to write. Get-EzEventFilter is here to make that easy:

# Minimal filter.
EzFilter -XPath -start (Date).addhours(-1) -end (Date)
# Advanced filter.
$Start = (Date).adddays(-1)
$End = Date
$PList = EzEvent -listProvider -Match 'PowerShell'
$Fltr = EzFilter -XPath -Start $Start -end $end -Provider $PList -Level 'Verbose'
Return $Fltr
# Easy filtering over the pipeline.
$Fltr | EzEvent

Note: all filter times are automatically converted to UTC.

c. Invoke-EzEventTracer
Last, but not least, Invoke-EzEventTracer. With this cmdlet, you can run commands and catch matching events with a one-liner. Events can be fetched from a custom .etl file or from an existing log. XPath filter is auto-generated. Almost too easy…

# Really easy trace (press ENTER to stop).
# Easy trace command/script and keep .etl trace.
EzTracer {Get-Date} -Provider 'Microsoft-Windows-PowerShell' -KeepTrace

The above command would return the following object:

Name    : EzTrace_20220308014711
Command : Get-Date
Result : 3/8/2022 5:47:13 AM
Event : {System.Diagnostics.Eventing.Reader.EventLogRecord, System.Diagnostics.Eventing.Reader.EventLogRecord,System.Diagnostics.Eventing.Reader.EventLogRecord, System.Diagnostics.Eventing.Reader.EventLogRecord…}
Start : 3/8/2022 1:47:12 PM
End : 3/8/2022 1:47:14 PM
Filter : @{XPath=*[System/TimeCreated[@SystemTime > ‘2022–03–08T13:47:12Z’ and @SystemTime < ‘2022–03–08T13:47:14Z’] and System/Provider[@Name=’Microsoft-Windows-PowerShell’] and System/Level<=4]; MDE=let StartTime = datetime(2022–03–08T13:47:12Z); let EndTime = datetime(2022–03–08T13:47:14Z);<UNKNOWN DATA SOURCE> | where StartTime <= Timestamp and Timestamp <= EndTime; Sentinel=let StartTime = datetime(2022–03–08T13:47:12Z);let EndTime = datetime(2022–03–08T13:47:14Z); <UNKNOWN DATA SOURCE> | where StartTime <= TimeGenerated and TimeGenerated <= EndTime}

This cmdlet has a few other switches to make your life really easy. Check the Help EzTracer pages if you are interested.

III. Closing words

Event Tracing for Windows can be used to trace almost anything in Windows. It’s a potential gold mine for security research (red & blue). Make sure to check out the following links if you want to dig deeper:

This might put you onto something. Who knows what you will catch next…

With this being said, that’s about it for today’s blog. Hope you found it useful! You can find the EzETW cmdlets here if you are interested.

Some of you might have noticed the EzFilter cmdlet also outputs KQL filters. Imagine piping these into a cmdlet to query Sentinel directly… really cool! But I guess that’s another tool, so probably another blog post…

Until then, take care… It was nice meeting you…

Happy hunting!

Knowledge center

Other articles

FalconHound, attack path management for blue teams

FalconHound, attack path management for blue teams

[dsm_breadcrumbs show_home_icon="off" separator_icon="K||divi||400" admin_label="Supreme Breadcrumbs" _builder_version="4.18.0" _module_preset="default" items_font="||||||||" items_text_color="rgba(255,255,255,0.6)" custom_css_main_element="color:...

Together. Secure. Today.

Stay in the loop and sign up to our newsletter

FalconForce realizes ambitions by working closely with its customers in a methodical manner, improving their security in the digital domain.

Energieweg 3
3542 DZ Utrecht
The Netherlands

FalconForce B.V.
[email protected]
(+31) 85 044 93 34

KVK 76682307
BTW NL860745314B01