Get-WinEvent: PowerShell's Secret Weapon for Mass Log Analysis

We've mastered individual log analysis and learned to tap into powerful telemetry sources. Now it's time to scale up. Welcome to Part 5 of our "Finding Evil" series, where we unlock the power of Get-WinEvent for analyzing thousands (or millions) of logs in seconds. This is where manual clicking in Event Viewer ends and real threat hunting begins.

This is Part 5 of my exploration through HackTheBox's "Finding Evil" mini-module, focusing on "Get-WinEvent." We'll learn how to query, filter, and analyze massive volumes of Windows event logs using PowerShell, then tackle the hands-on lab challenges.

(P.S. Scroll to the bottom if you just want the practical exercise walkthrough)

The Volume Problem

Let's be honest about the reality of Windows logging: enterprise environments generate millions of events daily. Security logs, Sysmon logs, application logs, system logs. The data is overwhelming.

Clicking through Event Viewer works fine when you're investigating a single suspicious event. But what if you need to:

  • Find all PowerShell executions with encoded commands across the last month
  • Identify every process that connected to a suspicious IP address
  • Track down all instances of a specific DLL being loaded
  • Correlate parent-child process relationships across thousands of events

Event Viewer isn't built for this. You need automation, and that's where Get-WinEvent becomes your best friend.

Get-WinEvent: The Basics

Get-WinEvent is PowerShell's native cmdlet for querying Windows Event Logs. It's fast, flexible, and works with everything: classic Windows logs, Sysmon, ETW providers, and even exported .evtx files.

Discovering What's Available

Before you can analyze logs, you need to know what exists on your system:

Get-WinEvent -ListLog * | Select-Object LogName, RecordCount, IsClassicLog, IsEnabled, LogMode, LogType | Format-Table -AutoSize

This reveals every log on your system with critical metadata:

  • RecordCount: How many events are stored
  • IsClassicLog: Whether it's classic (.evt) or modern (.evtx) format
  • IsEnabled: Whether logging is active
  • LogMode: Circular (overwrites old events) or other retention modes
  • LogType: Administrative, Operational, Analytical, or Debug

Suddenly you can see the logging landscape: which logs are filling up, which are barely used, and where your investigation targets live.

Understanding Providers

Remember ETW providers from Part 3? You can list all of them:

Get-WinEvent -ListProvider * | Format-Table -AutoSize

This shows which software components are generating events and which logs they feed into. Perfect for identifying overlooked telemetry sources.

Retrieving Events: The Core Techniques

Basic Log Retrieval

Get the 50 most recent System log events:

Get-WinEvent -LogName 'System' -MaxEvents 50 | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

This pulls events and formats them for readability. The pipeline (|) passes data between commands, letting you chain operations together.

Targeting Specific Logs

Want WinRM operational logs (tracking remote management activity)?

Get-WinEvent -LogName 'Microsoft-Windows-WinRM/Operational' -MaxEvents 30 | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

Getting the Oldest Events First

By default, Get-WinEvent returns newest events first. To investigate how an incident started:

Get-WinEvent -LogName 'Microsoft-Windows-WinRM/Operational' -Oldest -MaxEvents 30 | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

Analyzing Exported Logs

This is huge for incident response. Someone sends you a .evtx file from a compromised system:

Get-WinEvent -Path 'C:\Evidence\exported_log.evtx' -MaxEvents 5 | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

You can analyze forensic evidence without needing access to the original system.

Filtering: Where the Real Power Lives

FilterHashtable: Clean and Efficient

The FilterHashtable parameter lets you specify filtering criteria upfront, making queries super speedy:

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=1,3} | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

This retrieves only Sysmon Event IDs 1 (Process Creation) and 3 (Network Connection).

Why This Matters: If you see these events happening close together, you might be witnessing a process spawning and immediately calling home to a C2 server.

Date Range Filtering

Investigating activity during a specific timeframe:

$startDate = (Get-Date -Year 2023 -Month 5 -Day 28).Date
$endDate   = (Get-Date -Year 2023 -Month 6 -Day 3).Date
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=1,3; StartTime=$startDate; EndTime=$endDate} | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

Note: the end date is exclusive, so we specify June 3rd to include all of June 2nd.

Working with Exported Logs

Same filtering works on .evtx files:

Get-WinEvent -FilterHashtable @{Path='C:\Evidence\suspicious.evtx'; ID=1,3} | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

Perfect for analyzing evidence files during investigations.

Advanced Filtering: XML and XPath

Parsing Event Data with XML

Events store their detailed data in XML format. You can extract specific fields for precise analysis.

Scenario: A suspicious IP address (52.113.194.132) appeared in your network logs. Find all connections to it:

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=3} |
ForEach-Object {
    $xml = [xml]$_.ToXml()
    $eventData = $xml.Event.EventData.Data
    New-Object PSObject -Property @{
        SourceIP = $eventData | Where-Object {$_.Name -eq "SourceIp"} | Select-Object -ExpandProperty '#text'
        DestinationIP = $eventData | Where-Object {$_.Name -eq "DestinationIp"} | Select-Object -ExpandProperty '#text'
        ProcessGuid = $eventData | Where-Object {$_.Name -eq "ProcessGuid"} | Select-Object -ExpandProperty '#text'
        ProcessId = $eventData | Where-Object {$_.Name -eq "ProcessId"} | Select-Object -ExpandProperty '#text'
    }
} | Where-Object {$_.DestinationIP -eq "52.113.194.132"}

This script:

  1. Gets all network connection events
  2. Parses the XML for each event
  3. Extracts source IP, destination IP, process GUID, and process ID
  4. Filters for your suspicious IP

Now you have ProcessGuid values to trace back to the original malicious process.

Detecting Malicious .NET Assembly Loading

Remember our discussion about clr.dll and mscoree.dll indicating .NET execution? Let's find it:

$Query = @"
<QueryList>
    <Query Id="0">
        <Select Path="Microsoft-Windows-Sysmon/Operational">*[System[(EventID=7)]] and *[EventData[Data='mscoree.dll']] or *[EventData[Data='clr.dll']]
        </Select>
    </Query>
</QueryList>
"@
Get-WinEvent -FilterXml $Query | ForEach-Object {Write-Host $_.Message `n}

This XML query finds Sysmon Event ID 7 (Image Load) events where either mscoree.dll or clr.dll was loaded. When you see these DLLs loading into unusual processes, you've likely found malicious .NET execution.

XPath Queries: Surgical Precision

XPath lets you filter on specific event data fields.

Detecting Sysinternals Tool Installation:

When installing Sysinternals tools, users must accept the EULA, which writes to the registry:

Get-WinEvent -LogName 'Microsoft-Windows-Sysmon/Operational' -FilterXPath "*[EventData[Data[@Name='Image']='C:\Windows\System32\reg.exe']] and *[EventData[Data[@Name='CommandLine']='`"C:\Windows\system32\reg.exe`" ADD HKCU\Software\Sysinternals /v EulaAccepted /t REG_DWORD /d 1 /f']]" | Select-Object TimeCreated, ID, ProviderName, LevelDisplayName, Message | Format-Table -AutoSize

This finds every Sysinternals tool installation by looking for the exact EULA acceptance registry modification.

Finding Connections to Suspicious IPs:

Get-WinEvent -LogName 'Microsoft-Windows-Sysmon/Operational' -FilterXPath "*[System[EventID=3] and EventData[Data[@Name='DestinationIp']='52.113.194.132']]"

Precise targeting of network connections to a specific IP address.

Property-Based Filtering: Going Deeper

Understanding Event Properties

Every event has numerous properties. See them all:

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=1} -MaxEvents 1 | Select-Object -Property *

This reveals everything: TimeCreated, ProcessId, CommandLine, ParentImage, Hashes, and dozens more.

Hunting Encoded PowerShell Commands

Attackers love encoding PowerShell commands to evade detection. The -enc or -EncodedCommand parameter is a dead giveaway.

The Hunt:

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=1} | Where-Object {$_.Properties[21].Value -like "*-enc*"} | Format-List

Breaking this down:

  • $_.Properties[21].Value: Property index 21 is ParentCommandLine in Sysmon Event ID 1
  • -like "*-enc*": Wildcard search for "-enc" anywhere in the command line
  • Format-List: Display full details for easy analysis

Why Properties[21]?

You can find property indices by examining the XML representation of events in Event Viewer. The Properties array corresponds to the EventData fields in order.

When this query hits, you'll see full encoded PowerShell commands, often revealing obfuscated malicious scripts. The base64-encoded blobs can be decoded to expose the attacker's true intentions.

Real-World Investigation Workflow

Here's how these techniques combine in actual incident response:

Step 1: Identify the timeframe of suspicious activity

$startDate = (Get-Date -Year 2023 -Month 6 -Day 1).Date
$endDate   = (Get-Date -Year 2023 -Month 6 -Day 2).Date

Step 2: Find all processes created during that window

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=1; StartTime=$startDate; EndTime=$endDate} | Select-Object TimeCreated, ID, Message

Step 3: Filter for suspicious patterns (encoded commands, unusual parents)

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=1; StartTime=$startDate; EndTime=$endDate} | Where-Object {$_.Properties[21].Value -like "*-enc*"}

Step 4: Extract ProcessGuid for correlation

$suspiciousGuids = Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=1; StartTime=$startDate; EndTime=$endDate} | Where-Object {$_.Properties[21].Value -like "*-enc*"} | Select-Object -ExpandProperty Properties[4]

Step 5: Find all network connections from those processes

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; ID=3} | Where-Object {$suspiciousGuids -contains $_.Properties[2].Value}

This workflow transforms scattered logs into a coherent attack narrative.

The Power of Automation

What makes Get-WinEvent truly powerful is scriptability. You can:

  • Schedule automated scans for known IOCs
  • Build custom detection rules
  • Generate daily security reports
  • Feed results into SIEMs or other tools
  • Create reusable investigation templates

Manual log review catches single incidents. Automated Get-WinEvent queries catch entire campaigns.

Practical Exercises Walkthrough

Question 1) Utilize the Get-WinEvent cmdlet to traverse all event logs located within the "C:\Tools\chainsaw\EVTX-ATTACK-SAMPLES\Lateral Movement" directory and determine when the \\*\PRINT share was added. Enter the time of the identified event in the format HH:MM:SS as your answer.

Steps:

  1. RDP into target IP.
  2. Open Powershell. Enter Get-WinEvent cmdlet: Get-WinEvent -FilterHashtable @{Path='C:\Tools\chainsaw\EVTX-ATTACK-SAMPLES\Lateral Movement\*'} | Where-Object {$_.Id -eq "5142"} | Select-Object ID, TimeCreated, ProviderName
None

Quick explanation of the cmdlet: Get-WinEvent reads event data from EVTX files under the Lateral Movement folder. Event ID 5142 is the "network share added" event. The Select-Object statement makes the output more concise so it only displays relevant information.

And we get the answer from the output!

Answer: 12:30:30

Thanks for following along! Up next — Windows Event Logs & Finding Evil Skills Assessment.