SEC504: Hacker Tools, Techniques, and Incident Handling

Experience SANS training through course previews.
Learn MoreLet us help.
Contact usConnect, learn, and share with other cybersecurity professionals
Engage, challenge, and network with fellow CISOs in this exclusive community of security leaders
Become a member for instant access to our free resources.
Sign UpMission-focused cybersecurity training for government, defense, and education
Explore industry-specific programming and customized training solutions
Sponsor a SANS event or research paper
We're here to help.
Contact UsIn this article we'll start looking at working with the Windows event log using PowerShell.
Windows event logs are a valuable source of information for threat hunting, incident response, digital forensics, and a slew of other fields. PowerShell has powerful support for working with event log data, if not always intuitive or consistent.
Microsoft has two commands for interrogating Windows event logs: [code]Get-WinEvent[/code] and [code]Get-EventLog[/code].
The [code]Get-EventLog[/code] cmdlet uses a Win32 API that has been deprecated, so Microsoft recommends using [code]Get-WinEvent[/code]. However, Reddit wisdom indicates that [code]Get-WinEvent[/code] can sometimes be slower than [code]Get-EventLog[/code].
In practice, [code]Get-WinEvent[/code] is the preferred way to access event log information, since it is designed to support the modern Windows Event Log technology features. If you are working only with the Application, System, and Security logs, then [code]Get-EventLog[/code] may still work for you, but as a deprecated API there's no guarantee Microsoft will continue to make [code]Get-EventLog[/code] available in the future.
Microsoft has many sources of event log data, both those included with Windows and from third-party applications. To list the available event log sources, run [code]Get-WinEvent -ListLog *[/code]:
PS C:\Windows\system32> Get-WinEvent -ListLog *
LogMode MaximumSizeInBytes RecordCount LogName
------- ------------------ ----------- -------
Circular 15728640 195 Windows PowerShell
Circular 1052672 0 ThinPrint Diagnostics
Circular 20971520 268 System
Circular 20971520 9986 Security
Circular 20971520 0 Key Management Service
Circular 1052672 0 Internet Explorer
Circular 20971520 0 HardwareEvents
Circular 20971520 840 Application
Circular 1052672 Windows Networking Vpn Plugin Platform/OperationalVerbose
Circular 1052672 Windows Networking Vpn Plugin Platform/Operational
Circular 1052672 0 SMSApi
Circular 1052672 0 Setup
Circular 15728640 0 PowerShellCore/Operational
Circular 1052672 0 OpenSSH/Operational
Circular 1052672 0 OpenSSH/Admin
Circular 1052672 Network Isolation Operational
Circular 1052672 0 Microsoft-WindowsPhone-Connectivity-WiFiConnSvc-Channel
Circular 1052672 0 Microsoft-Windows-WWAN-SVC-Events/Operational
Circular 1052672 0 Microsoft-Windows-WPD-MTPClassDriver/Operational
Circular 1052672 0 Microsoft-Windows-WPD-CompositeClassDriver/Operational
Circular 1052672 0 Microsoft-Windows-WPD-ClassInstaller/Operational
Circular 1052672 0 Microsoft-Windows-Workplace Join/Admin
Circular 1052672 Microsoft-Windows-Wordpad/Admin
Circular 1052672 221 Microsoft-Windows-WMI-Activity/Operational
...
A lot of the event log sources on my test system are empty. We can use the pipeline and [code]Where-Object[/code] to filter the results to show log sources where logging events are available:
PS C:\Windows\system32> Get-WinEvent -ListLog * | Where-Object -Property RecordCount -GT 0 | Select-Object -Property LogName, RecordCount
LogName RecordCount
------- -----------
Windows PowerShell 195
System 268
Security 9988
Application 841
Microsoft-Windows-WMI-Activity/Operational 221
Microsoft-Windows-WinRM/Operational 8
Microsoft-Windows-Winlogon/Operational 86
Microsoft-Windows-WindowsSystemAssessmentTool/Operational 38
Microsoft-Windows-Windows Firewall With Advanced Security/Firewall 22
Microsoft-Windows-Windows Defender/Operational 177
...
Microsoft-Windows-AppModel-Runtime/Admin 18
Microsoft-Windows-AppLocker/EXE and DLL 16
Microsoft-Windows-Application-Experience/Program-Telemetry 2
Microsoft-Windows-AppID/Operational 10
Microsoft-Client-Licensing-Platform/Admin 337
Let's break down this command piece-by-piece:
In this example we retrieved all of the event log sources using [code]-Listlog [/code], but we can also supply a more specific keyword for filtering. Personally, I can never remember the exact event log name for AppLocker, so I use the [code]applocker*[/code] string to match that name:
PS C:\windows\system32> Get-WinEvent -ListLog *applocker* | Where-Object -Property RecordCount -GT 0 | Select-Object -Property LogName, RecordCount
LogName RecordCount
------- -----------
Microsoft-Windows-AppLocker/EXE and DLL 17
To get log data, specify the event log name with [code]Get-WinEvent -LogName[/code]. Let's take a look for the Security event log:
PS C:\Windows\system32> Get-WinEvent -LogName Security
ProviderName: Microsoft-Windows-Security-Auditing
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
7/12/2022 11:51:43 AM 4616 Information The system time was changed....
7/12/2022 11:48:32 AM 4672 Information Special privileges assigned to new logon....
7/12/2022 11:48:32 AM 4624 Information An account was successfully logged on....
7/12/2022 11:47:41 AM 4672 Information Special privileges assigned to new logon....
7/12/2022 11:47:41 AM 4624 Information An account was successfully logged on....
7/12/2022 11:46:48 AM 5379 Information Credential Manager credentials were read....
7/12/2022 11:46:48 AM 5379 Information Credential Manager credentials were read....
7/12/2022 11:46:48 AM 5379 Information Credential Manager credentials were read....
...
By default, [code]Get-WinEvent[/code] will display the [code]TimeCreated[/code], [code]Id[/code], [code]LevelDisplayName[/code], and [code]Message[/code] fields. This output will be truncated unless you have a very small font or a very wide PowerShell window. I will often use [code]Format-List[/code] to see the results with each property is listed on a new line:
PS C:\Windows\system32> Get-WinEvent -LogName Security | Format-List
TimeCreated : 7/12/2022 12:59:24 PM
ProviderName : Microsoft-Windows-Security-Auditing
Id : 5379
Message : Credential Manager credentials were read.
Subject:
Security ID: S-1-5-18
Account Name: SEC504STUDENT$
Account Domain: SEC504
Logon ID: 0x3E7
Read Operation: Enumerate Credentials
This event occurs when a user performs a read operation on stored
credentials in Credential Manager.
TimeCreated : 7/12/2022 12:59:24 PM
ProviderName : Microsoft-Windows-Security-Auditing
Id : 5379
...
We can look at the available properties for the [code]Get-WinEvent[/code] output using [code]Get-Member[/code]:
PS C:\Windows\system32> Get-WinEvent -LogName Security -MaxEvents 1 | Get-Member -MemberType Property
TypeName: System.Diagnostics.Eventing.Reader.EventLogRecord
Name MemberType Definition
---- ---------- ----------
ActivityId Property System.Nullable[guid] ActivityId {get;}
Bookmark Property System.Diagnostics.Eventing.Reader.EventBookmark Bookmark {get;}
ContainerLog Property string ContainerLog {get;}
Id Property int Id {get;}
Keywords Property System.Nullable[long] Keywords {get;}
KeywordsDisplayNames Property System.Collections.Generic.IEnumerable[string] KeywordsDisplayNames {get;}
Level Property System.Nullable[byte] Level {get;}
LevelDisplayName Property string LevelDisplayName {get;}
LogName Property string LogName {get;}
MachineName Property string MachineName {get;}
MatchedQueryIds Property System.Collections.Generic.IEnumerable[int] MatchedQueryIds {get;}
Opcode Property System.Nullable[int16] Opcode {get;}
OpcodeDisplayName Property string OpcodeDisplayName {get;}
ProcessId Property System.Nullable[int] ProcessId {get;}
Properties Property System.Collections.Generic.IList[System.Diagnostics.Eventing.Reader.EventProperty] Properties {get;}
ProviderId Property System.Nullable[guid] ProviderId {get;}
ProviderName Property string ProviderName {get;}
Qualifiers Property System.Nullable[int] Qualifiers {get;}
RecordId Property System.Nullable[long] RecordId {get;}
RelatedActivityId Property System.Nullable[guid] RelatedActivityId {get;}
Task Property System.Nullable[int] Task {get;}
TaskDisplayName Property string TaskDisplayName {get;}
ThreadId Property System.Nullable[int] ThreadId {get;}
TimeCreated Property System.Nullable[datetime] TimeCreated {get;}
UserId Property System.Security.Principal.SecurityIdentifier UserId {get;}
Version Property System.Nullable[byte] Version {get;}
In this example I added [code]-MaxEvents 1[/code] to the [code]Get-WinEvent[/code] command. Without this, [code]Get-WinEvent[/code] will retrieve all events from the specified Security event log source before sending the output to the pipeline and [code]Get-Member[/code] (e.g., it will be slow, and probably require a lot of RAM). Instructing [code]Get-WinEvent[/code] to stop collecting events after the first event gets the property information we want without waiting to collect all event log data.
[code]Get-WinEvent[/code] shows a lot of available property information for us to work with. I will often get property information by looking at the actual values with [code]Select-Object[/code] as well:
PS C:\Windows\system32> Get-WinEvent -LogName Security -MaxEvents 1 | Select-Object -Property *
Message : Credential Manager credentials were read.
Security ID: S-1-5-18
Account Name: SEC504STUDENT$
Account Domain: SEC504
Logon ID: 0x3E7
Read Operation: Enumerate Credentials
This event occurs when a user performs a read operation on stored credentials in Credential Manager.
Id : 5379
Version : 0
Qualifiers :
Level : 0
Task : 13824
Opcode : 0
Keywords : -9214364837600034816
RecordId : 31056
ProviderName : Microsoft-Windows-Security-Auditing
ProviderId : 54849625-5478-4994-a5ba-3e3b0328c30d
LogName : Security
ProcessId : 672
ThreadId : 4652
MachineName : Sec504Student
UserId :
TimeCreated : 7/12/2022 12:02:31 PM
ActivityId : 4352e2d2-8bc2-0001-59e3-5243c28bd801
RelatedActivityId :
ContainerLog : Security
MatchedQueryIds : {}
Bookmark : System.Diagnostics.Eventing.Reader.EventBookmark
LevelDisplayName : Information
OpcodeDisplayName : Info
TaskDisplayName : User Account Management
KeywordsDisplayNames : {Audit Success}
Properties : {System.Diagnostics.Eventing.Reader.EventProperty, System.Diagnostics.Eventing.Reader.EventProperty,
System.Diagnostics.Eventing.Reader.EventProperty, System.Diagnostics.Eventing.Reader.EventProperty...}
Seeing the properties that are available, we can query the event log for specific properties using [code]Where-Object[/code]. For example, we can look for any event information for event ID 1102 (The audit log was cleared):
PS C:\Windows\system32> Get-WinEvent -LogName Security | Where-Object -Property Id -EQ 1102 | Format-List -Property TimeCreated,Message
TimeCreated : 6/26/2022 10:34:08 AM
Message : The audit log was cleared.
Subject:
Security ID: S-1-5-21-2977773840-2930198165-1551093962-1000
Account Name: Sec504
Domain Name: SEC504STUDENT
Logon ID: 0x1BD38
Although you can use the pipeline to filter the results of [code]Get-WinEvent[/code], it's not ideal. The PowerShell pipeline works in serial, where each command in the pipeline has to complete execution before sending the data to the next command. For event logs with lots of logging data, this can take a long time and uses up more RAM (and disk I/O) than absolutely necessary.
[code]Get-WinEvent[/code] can filter using a filter hash table. A hash table (a.k.a. associative array or dictionary) is a mechanism to specify properties and values. When used with the [code]-FilterHashTable[/code] option, we can specify attributes to filter the events returned in an optimal manner without relying on the PowerShell pipeline.
PS C:\Windows\system32> Get-WinEvent -FilterHashtable @{LogName='Security'; ID=1102 } | Format-List -Property TimeCreated,Message
TimeCreated : 6/26/2022 10:34:08 AM
Message : The audit log was cleared.
Subject:
Security ID: S-1-5-21-2977773840-2930198165-1551093962-1000
Account Name: Sec504
Domain Name: SEC504STUDENT
Logon ID: 0x1BD38
Let's break down this command step-by-step:
Here's why you should use [code]-FilterHashTable[/code]:
Next, let's look at an example of how you might apply using the filter hash table in practice during an incident.
[code]Get-WinEvent[/code] accepts a filter hash table with multiple values (adapted from the Microsoft documentation):
Key name | Data type | Wildcard Support |
---|---|---|
LogName | <String[]> | Yes |
ProviderName | <String[]> | Yes |
Path | <String[]> | No |
Keywords | <Long[]> | No |
ID | <Int32[]> | No |
Level | <Int32[]> | No |
StartTime | <DateTime> | No |
EndTime | <DateTime> | No |
UserID | <SID> | No |
Data | <String[]> | No |
<named-data> | <String[]> | No |
In an incident, you may want to interrogate logging events between a specific start and end date range. First, specify the start and end dates using [code]Get-Date[/code]:
PS C:\Windows\System32> $startDate = Get-Date 7/1/2022
PS C:\Windows\System32> $endDate = Get-Date 7/12/2022
PS C:\Windows\System32>
Next, Run [code]Get-WinEvent[/code] with a hash table, specifying the [code]LogName[/code], [code]StartTime[/code], and [code]EndTime[/code] elements:
PS C:\Windows\System32> Get-WinEvent -FilterHashtable @{LogName='Security'; StartTime=$startDate; EndTime=$endDate}
ProviderName: Microsoft-Windows-Security-Auditing
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
7/11/2022 9:58:15 PM 5379 Information Credential Manager credentials were read....
7/11/2022 9:58:15 PM 5379 Information Credential Manager credentials were read....
7/11/2022 9:58:15 PM 5379 Information Credential Manager credentials were read....
7/11/2022 9:58:15 PM 5379 Information Credential Manager credentials were read....
7/11/2022 9:58:15 PM 5379 Information Credential Manager credentials were read....
7/11/2022 9:58:15 PM 5379 Information Credential Manager credentials were read....
7/11/2022 9:58:15 PM 5379 Information Credential Manager credentials were read....
...
7/1/2022 8:10:44 PM 4624 Information An account was successfully logged on....
7/1/2022 8:03:46 PM 4672 Information Special privileges assigned to new logon....
7/1/2022 8:03:46 PM 4624 Information An account was successfully logged on....
7/1/2022 7:58:10 PM 4672 Information Special privileges assigned to new logon....
7/1/2022 7:58:10 PM 4624 Information An account was successfully logged on....
In the next article on Working with the PowerShell Event Log, we'll dig into more practical ways to apply these PowerShell skills for threat hunting. Stay tuned!
-Joshua Wright
Return to Getting Started With PowerShell
Joshua Wright is the author of SANS SEC504: Hacker Tools, Techniques, and Incident Handling, a faculty fellow for the SANS Institute, and a senior technical director at Counter Hack.
As Senior Technical Director at Counter Hack and SANS Faculty Fellow, Joshua has advanced cybersecurity through ethical penetration testing, uncovering critical vulnerabilities across Fortune 500 companies and national infrastructure providers.
Read more about Joshua Wright