P09: Windows Event Log Analyzer

P09: Windows Event Log Analyzer

A comprehensive guide to building a security event monitoring and analysis tool with PowerShell


The Core Question You’re Answering

“How can I systematically detect security threats and anomalies by analyzing Windows Event Logs, transforming raw log data into actionable security intelligence?”

This project addresses a fundamental challenge in security operations: Windows generates thousands of events daily, but only a small fraction indicate actual security concerns. Your analyzer must efficiently sift through this noise to identify patterns like brute force attacks, privilege escalation, and unauthorized access attempts.

The deeper questions you’ll answer:

  • How do you query millions of events without exhausting system resources?
  • How do you correlate isolated events into meaningful attack narratives?
  • How do you distinguish normal administrative activity from malicious behavior?
  • How do you build detection rules that minimize false positives while catching real threats?

Concepts You Must Understand First

Before diving into implementation, ensure you understand these foundational concepts:

1. Windows Event Log Fundamentals

  • Event Channels: Security, System, Application, and custom logs
  • Event Structure: Provider, EventID, Level, TimeCreated, EventData
  • Audit Policies: What events Windows actually records (and what it doesn’t by default)

2. Security Event Categories

  • Account Logon Events: 4624 (success), 4625 (failure), 4648 (explicit credentials)
  • Account Management: 4720 (created), 4726 (deleted), 4740 (locked)
  • Privilege Use: 4672 (special privileges), 4673 (privilege service)
  • Process Events: 4688 (process creation), 4689 (process termination)

3. XPath Query Language

  • Basic path expressions and predicates
  • Time-based filtering with timediff()
  • Combining multiple conditions with and/or

4. Attack Pattern Recognition

  • Brute Force: High volume of failed logins from single source
  • Password Spraying: Single password tried against multiple accounts
  • Privilege Escalation: Normal user gaining admin rights
  • Lateral Movement: Access spreading across systems

5. PowerShell Performance Patterns

  • Pipeline processing vs. collecting results
  • Get-WinEvent vs. Get-EventLog (and why you should use the former)
  • FilterHashtable vs. FilterXPath (tradeoffs)

Project Overview

What you’ll build: A tool that queries Windows Event Logs, filters for security-relevant events (failed logins, service crashes, privilege escalation), and generates alerts or reports.

Attribute Value
Difficulty Advanced
Time Estimate 2 weeks
Programming Language PowerShell
Knowledge Area Security Operations, Forensics
Prerequisites Intermediate PowerShell, Windows security concepts

Learning Objectives

After completing this project, you will be able to:

  1. Query event logs efficiently - Use Get-WinEvent with optimized filters
  2. Build XPath queries - Filter events at the source for performance
  3. Understand security events - Interpret Windows security event IDs
  4. Correlate events - Link related events across time and logs
  5. Generate actionable alerts - Threshold-based anomaly detection
  6. Create security reports - Formatted output for security teams

Real World Outcome

When this project is complete, you will have a comprehensive security event monitoring system that transforms raw Windows Event Logs into actionable security intelligence. Here is exactly what you will see and experience:

What You’re Building: The Complete Picture

+============================================================================+
||               WINDOWS EVENT LOG ANALYZER - COMPLETE SYSTEM                ||
+============================================================================+
|                                                                             |
|  +---------------------------+     +---------------------------+            |
|  |     PowerShell Module     |     |    Scheduled Task         |            |
|  |   SecurityEventAnalyzer   |     |   (runs every hour)       |            |
|  +---------------------------+     +---------------------------+            |
|              |                                  |                           |
|              v                                  v                           |
|  +----------------------------------------------------------------+        |
|  |                    CORE ANALYSIS ENGINE                         |        |
|  |                                                                  |        |
|  |  [Query Layer]  -->  [Parse Layer]  -->  [Correlate]  --> [Report] |   |
|  |                                                                  |        |
|  +----------------------------------------------------------------+        |
|              |                                                              |
|              +---------------------+----------------------+                 |
|              |                     |                      |                 |
|              v                     v                      v                 |
|  +------------------+  +-------------------+  +---------------------+       |
|  | Terminal Output  |  | HTML Report       |  | Email Alert         |       |
|  | (live analysis)  |  | (daily summary)   |  | (critical findings) |       |
|  +------------------+  +-------------------+  +---------------------+       |
|                                                                             |
+==============================================================================+

Terminal Experience: Live Analysis

When you run the analyzer from PowerShell, here’s exactly what you’ll see:

PS C:\Scripts> Import-Module .\SecurityEventAnalyzer.psm1

PS C:\Scripts> Analyze-SecurityEvents -Hours 24 -Verbose

VERBOSE: Starting security event analysis for last 24 hours
VERBOSE: Querying Security log with optimized filter...
VERBOSE: Retrieved 892 security events in 2.3 seconds
VERBOSE: Querying System log for service events...
VERBOSE: Retrieved 45 system events in 0.8 seconds
VERBOSE: Parsing event data...
VERBOSE: Running correlation engine...

================================================================================
                    SECURITY EVENT ANALYSIS REPORT
                    Generated: 2025-12-26 14:32:45
                    Analysis Period: Last 24 hours
================================================================================

CRITICAL FINDINGS (3)
--------------------

[CRITICAL] BRUTE FORCE ATTACK DETECTED
  Time Range:    2025-12-26 02:14:00 - 02:14:30
  Source IP:     192.168.1.105
  Target:        jsmith@CORP
  Attempts:      47 failed logins in 30 seconds
  Status Codes:  0xC000006D (bad password)
  Action:        Block IP immediately, investigate account

[CRITICAL] PRIVILEGE ESCALATION
  Time:          2025-12-26 10:23:15
  User:          mwilson@CORP
  Privileges:    SeDebugPrivilege, SeTakeOwnershipPrivilege
  Context:       User not in Administrators group
  Action:        Investigate user activity, check for compromise

[CRITICAL] ACCOUNT LOCKED - POSSIBLE ATTACK
  Time:          2025-12-26 02:14:35
  Account:       jsmith@CORP
  Lock Reason:   Too many failed attempts
  Source:        192.168.1.105
  Action:        Verify with user, check if legitimate

WARNING FINDINGS (8)
-------------------

[WARNING] Multiple failed logins - below threshold
  User:          admin@CORP
  Count:         7 failures in 10 minutes
  Source:        10.0.0.50 (internal)
  Note:          May be misconfigured service

[WARNING] Service stopped unexpectedly
  Service:       Windows Defender (WinDefend)
  Time:          2025-12-26 08:45:22
  Occurrences:   3 times in 24 hours
  Action:        Check service health, may indicate tampering

[WARNING] Explicit credential use detected
  User:          svc_backup
  Time:          2025-12-26 11:30:00
  Target:        \\FILESERVER01\backup$
  Note:          Verify if scheduled task or manual

... (5 more warnings)

SUMMARY STATISTICS
------------------
  Total Events Analyzed:     12,345
  Security Events:              892
  System Events:                 45
  Application Events:           123

  By Severity:
    Critical:                     3
    Warning:                      8
    Informational:              926

  Authentication:
    Successful Logins:        4,523
    Failed Logins:              167
    Account Lockouts:             2

  Top Failed Login Sources:
    192.168.1.105              47 attempts
    10.0.0.50                   7 attempts
    192.168.1.200               5 attempts

================================================================================
Report saved to: C:\Reports\SecurityAnalysis_2025-12-26.html
Analysis completed in 8.4 seconds
================================================================================

PowerShell Commands You’ll Have

#------------------------------------------------------------------------------
# COMMAND 1: Get-SecurityEvents - Query and parse security events
#------------------------------------------------------------------------------

PS> Get-SecurityEvents -EventId 4625 -Hours 1

TimeCreated         EventId EventType   Account     SourceIP        LogonType
-----------         ------- ---------   -------     --------        ---------
12/26/2025 14:25:33    4625 FailedLogin jsmith      192.168.1.105   3
12/26/2025 14:25:34    4625 FailedLogin jsmith      192.168.1.105   3
12/26/2025 14:25:35    4625 FailedLogin jsmith      192.168.1.105   3
...

# With verbose output showing query performance
PS> Get-SecurityEvents -EventId 4625 -Hours 24 -Verbose

VERBOSE: Building FilterHashtable query
VERBOSE: Query: @{LogName='Security'; ID=4625; StartTime='12/25/2025 14:30:00'}
VERBOSE: Executing query...
VERBOSE: Retrieved 167 events in 1.2 seconds
VERBOSE: Parsing EventData XML...
VERBOSE: Parsed 167 events in 0.3 seconds

#------------------------------------------------------------------------------
# COMMAND 2: Find-BruteForceAttempts - Detect brute force patterns
#------------------------------------------------------------------------------

PS> Find-BruteForceAttempts -Hours 24 -Threshold 10 -WindowMinutes 5

Type         : BruteForce
Severity     : Critical
SourceIP     : 192.168.1.105
TargetAccounts : jsmith, admin
StartTime    : 12/26/2025 02:14:00
EndTime      : 12/26/2025 02:14:30
AttemptCount : 47
Description  : 47 failed logins from 192.168.1.105 in 5 minutes
Action       : Block IP, investigate targeted accounts

#------------------------------------------------------------------------------
# COMMAND 3: Find-PrivilegeEscalation - Detect privilege abuse
#------------------------------------------------------------------------------

PS> Find-PrivilegeEscalation -Hours 24

Type            : PrivilegeEscalation
Severity        : Critical
TimeCreated     : 12/26/2025 10:23:15
Account         : mwilson
Domain          : CORP
PrivilegesGranted : SeDebugPrivilege, SeTakeOwnershipPrivilege
Description     : Normal user mwilson received sensitive privileges
Action          : Investigate account activity, verify if authorized

#------------------------------------------------------------------------------
# COMMAND 4: Get-ServiceAnomalies - Monitor service health
#------------------------------------------------------------------------------

PS> Get-ServiceAnomalies -Hours 24

ServiceName     : WinDefend
DisplayName     : Windows Defender
EventType       : Unexpected Stop
Occurrences     : 3
LastOccurrence  : 12/26/2025 08:45:22
Severity        : Warning
Pattern         : Multiple stops may indicate tampering

#------------------------------------------------------------------------------
# COMMAND 5: New-SecurityReport - Generate comprehensive report
#------------------------------------------------------------------------------

PS> New-SecurityReport -Date (Get-Date).AddDays(-1) -OutputPath "C:\Reports"

Generating security report for 2025-12-25...
  Querying Security log... done (2.1s)
  Querying System log... done (0.4s)
  Running brute force detection... done (0.8s)
  Running privilege escalation detection... done (0.5s)
  Analyzing service anomalies... done (0.2s)
  Generating HTML report... done

Report generated successfully!
  Path: C:\Reports\SecurityReport_2025-12-25.html
  Size: 45 KB
  Findings: 3 critical, 8 warnings

PS> Invoke-Item "C:\Reports\SecurityReport_2025-12-25.html"

HTML Report Output

When you generate a report, it creates a professional HTML file:

+============================================================================+
||                    SECURITY REPORT - 2025-12-25                          ||
+============================================================================+
|                                                                            |
|  +----------------------+  +----------------------+  +------------------+  |
|  |    12,345            |  |      4,523           |  |       167        |  |
|  |    Total Events      |  |  Successful Logins   |  |  Failed Logins   |  |
|  +----------------------+  +----------------------+  +------------------+  |
|                                                                            |
|  CRITICAL FINDINGS                                                         |
|  ================                                                          |
|                                                                            |
|  +------------------------------------------------------------------------+|
|  | BRUTE FORCE ATTACK DETECTED                                            ||
|  |------------------------------------------------------------------------|
|  | Source IP:      192.168.1.105                                          ||
|  | Target:         jsmith@CORP                                            ||
|  | Attempts:       47 in 30 seconds                                       ||
|  | Time:           02:14:00 - 02:14:30                                    ||
|  | Action:         Block IP, investigate account                          ||
|  +------------------------------------------------------------------------+|
|                                                                            |
|  TOP FAILED LOGIN SOURCES                                                  |
|  ========================                                                  |
|  | Source IP       | Attempts | Target Accounts   | First Seen | Last   | |
|  |-----------------|----------|-------------------|------------|--------|  |
|  | 192.168.1.105   | 47       | jsmith            | 02:14:00   | 02:14:30|  |
|  | 10.0.0.50       | 7        | admin, svc_app    | 08:00:00   | 08:10:00|  |
|                                                                            |
|  Generated by SecurityEventAnalyzer v1.0 at 2025-12-26 08:00:00           |
+============================================================================+

Email Alert Format

When critical findings are detected:

From: security-alerts@company.com
To: soc-team@company.com
Subject: [CRITICAL] 3 Security Findings Detected - WORKSTATION01

================================================================================
                    SECURITY ALERT - IMMEDIATE ACTION REQUIRED
================================================================================

Server: WORKSTATION01.corp.local
Time: December 26, 2025 14:32:45

CRITICAL FINDINGS (3)
---------------------

1. BRUTE FORCE ATTACK
   Source: 192.168.1.105
   Target: jsmith@CORP
   47 attempts in 30 seconds
   ACTION: Block IP, check account status

2. PRIVILEGE ESCALATION
   User: mwilson@CORP
   Privileges: SeDebugPrivilege, SeTakeOwnershipPrivilege
   ACTION: Investigate immediately

Full report: C:\Reports\SecurityReport_2025-12-26.html
================================================================================

File Structure You’ll Create

C:\Scripts\SecurityEventAnalyzer\
|
+-- SecurityEventAnalyzer.psm1      # Main module with all functions
+-- SecurityEventAnalyzer.psd1      # Module manifest
|
+-- Private\                        # Internal helper functions
|   +-- ConvertTo-SecurityEvent.ps1
|   +-- Build-XPathQuery.ps1
|
+-- Public\                         # Exported commands
|   +-- Get-SecurityEvents.ps1
|   +-- Find-BruteForceAttempts.ps1
|   +-- Find-PrivilegeEscalation.ps1
|   +-- New-SecurityReport.ps1
|
+-- Config\
|   +-- EventDefinitions.json       # Event ID metadata
|   +-- Thresholds.json             # Detection thresholds

Deep Dive: Theoretical Foundation

Windows Event Log Architecture

Windows Event Logs are stored as .evtx files and managed by the Event Log service. Understanding this architecture is essential for efficient querying and security analysis.

Book Reference: Windows Security Internals by James Forshaw, Part 2: “Auditing and Logging” - provides deep coverage of how Windows generates and stores security events.

The Event Log Service Architecture

+============================================================================+
||                    WINDOWS EVENT LOG ARCHITECTURE                         ||
+============================================================================+
|                                                                            |
|  +------------------------+    +------------------------+                  |
|  |   APPLICATIONS         |    |   WINDOWS KERNEL       |                  |
|  |   (Your apps, services)|    |   (LSA, SAM, etc.)     |                  |
|  +----------+-------------+    +----------+-------------+                  |
|             |                             |                                |
|             | ETW (Event Tracing          | Security Events               |
|             | for Windows)                | (4624, 4625, etc.)            |
|             |                             |                                |
|             v                             v                                |
|  +------------------------------------------------------------------+     |
|  |                     EVENT LOG SERVICE                             |     |
|  |                     (wevtsvc.dll / svchost.exe)                   |     |
|  |                                                                    |     |
|  |  +-------------------+  +-------------------+  +----------------+  |     |
|  |  | Event Buffer      |  | Event Buffer      |  | Event Buffer   |  |     |
|  |  | (Application)     |  | (Security)        |  | (System)       |  |     |
|  |  +-------------------+  +-------------------+  +----------------+  |     |
|  |                                                                    |     |
|  |  +----------------------------------------------------------+     |     |
|  |  |              EVENT CHANNEL DISPATCHER                     |     |     |
|  |  |  - Receives events from producers                         |     |     |
|  |  |  - Routes to appropriate channel                          |     |     |
|  |  |  - Manages subscriptions                                  |     |     |
|  |  +----------------------------------------------------------+     |     |
|  +------------------------------------------------------------------+     |
|             |                             |                                |
|             v                             v                                |
|  +------------------------------------------------------------------+     |
|  |                    .EVTX FILE STORAGE                             |     |
|  |            %SystemRoot%\System32\winevt\Logs\                     |     |
|  |                                                                    |     |
|  |  +-------------------+  +-------------------+  +----------------+  |     |
|  |  | Application.evtx  |  | Security.evtx    |  | System.evtx    |  |     |
|  |  | (configurable     |  | (default: 20MB)  |  | (configurable  |  |     |
|  |  |  max size)        |  | (circular/auto-  |  |  max size)     |  |     |
|  |  |                   |  |  backup)         |  |                |  |     |
|  |  +-------------------+  +-------------------+  +----------------+  |     |
|  +------------------------------------------------------------------+     |
|                                                                            |
+============================================================================+

Log Types and Their Contents

Log Location Contents Security Relevance
Security Security.evtx Authentication, authorization, policy changes Critical - primary security log
System System.evtx OS and driver events, service state changes High - service tampering detection
Application Application.evtx Application errors, warnings, information Medium - application-level anomalies
PowerShell PowerShell-Operational.evtx Script execution, module loading Critical - command & control detection
Sysmon Sysmon-Operational.evtx Process creation, network, file changes Critical - if installed, best visibility

Event Structure Deep Dive

                         WINDOWS EVENT STRUCTURE
  +====================================================================+
  ||                    EVENT XML SCHEMA                               ||
  +====================================================================+
  |                                                                     |
  |  <Event xmlns="...">                                                |
  |    |                                                                |
  |    +-- <System>  [METADATA - same structure for ALL events]        |
  |    |     |                                                          |
  |    |     +-- <Provider Name="..." Guid="..."/>                     |
  |    |     |     ^-- Who generated the event                         |
  |    |     |                                                          |
  |    |     +-- <EventID>4625</EventID>                               |
  |    |     |     ^-- THE KEY! Identifies what happened               |
  |    |     |                                                          |
  |    |     +-- <Level>0</Level>                                      |
  |    |     |     ^-- 0=Info, 1=Critical, 2=Error, 3=Warning          |
  |    |     |                                                          |
  |    |     +-- <TimeCreated SystemTime="2025-12-26T10:23:45.123Z"/>  |
  |    |     |     ^-- UTC timestamp with millisecond precision        |
  |    |     |                                                          |
  |    |     +-- <Computer>WORKSTATION01.corp.local</Computer>         |
  |    |           ^-- FQDN of machine that generated event            |
  |    |                                                                |
  |    +-- <EventData>  [PAYLOAD - varies by EventID]                  |
  |          |                                                          |
  |          +-- <Data Name="TargetUserName">jsmith</Data>             |
  |          +-- <Data Name="TargetDomainName">CORP</Data>             |
  |          +-- <Data Name="Status">0xC000006D</Data>                 |
  |          +-- <Data Name="IpAddress">192.168.1.105</Data>           |
  |          |                                                          |
  |          ^-- Different fields for each EventID!                    |
  |                                                                     |
  +=====================================================================+

The Security Event Generation Pipeline

                    HOW SECURITY EVENTS ARE GENERATED
  +====================================================================+
  |                                                                     |
  |  USER ACTION                 KERNEL PROCESSING           EVENT      |
  |  ===========                 =================           =====      |
  |                                                                     |
  |  [User types password]                                              |
  |         |                                                           |
  |         v                                                           |
  |  +-------------+                                                    |
  |  | WinLogon.exe|                                                    |
  |  +------+------+                                                    |
  |         |                                                           |
  |         | LsaLogonUser()                                            |
  |         v                                                           |
  |  +-------------+     +-------------+     +------------------+       |
  |  | LSASS.exe   |---->| SAM / AD    |---->| Validate Creds   |       |
  |  | (Local      |     | (Account    |     | (Check password, |       |
  |  |  Security   |     |  Database)  |     |  policy, lockout)|       |
  |  |  Authority) |     +-------------+     +--------+---------+       |
  |  +------+------+                                  |                 |
  |         |                                         |                 |
  |         |  <---- AUDIT DECISION POINT ----        |                 |
  |         |        (Is auditing enabled?)           |                 |
  |         v                                         v                 |
  |  +--------------------------------------------------+              |
  |  |              SECURITY EVENT GENERATED            |              |
  |  |                                                  |              |
  |  |   Success? --> EventID 4624 (Logon Success)     |              |
  |  |   Failure? --> EventID 4625 (Logon Failure)     |              |
  |  +------------------------+-------------------------+              |
  |                           |                                        |
  |                           v                                        |
  |                    +-------------+                                 |
  |                    | Security.   |                                 |
  |                    | evtx        |                                 |
  |                    +-------------+                                 |
  |                                                                     |
  +=====================================================================+

  KEY INSIGHT: Events are only generated if AUDIT POLICY is enabled!

  Check your audit policy:
  > auditpol /get /category:*

Critical Security Event IDs

Authentication events (Security log):

Event ID Description Severity
4624 Successful logon Info
4625 Failed logon Warning
4634 Logoff Info
4648 Explicit credential logon Warning
4672 Special privileges assigned Info/Warning
4720 User account created Info
4726 User account deleted Warning
4740 Account locked out Critical

Logon types:

Type Description Risk Level
2 Interactive (console) Low
3 Network (file share) Medium
4 Batch (scheduled task) Low
5 Service Low
7 Unlock Low
8 NetworkCleartext High
10 RemoteInteractive (RDP) Medium
11 CachedInteractive Low

Process and service events:

Event ID Log Description
7034 System Service crashed
7035 System Service start/stop
7036 System Service state change
4688 Security Process created (if auditing enabled)
4689 Security Process terminated

XPath Filtering: Performance at Scale

Book Reference: PowerShell in Depth by Don Jones, Jeffrey Hicks, and Richard Siddaway, Chapter 21: “Working with Events” - covers efficient event querying techniques.

The Performance Problem Visualized

                        NAIVE QUERY (Where-Object)
  +====================================================================+
  |                                                                     |
  |   DISK                    MEMORY                   YOUR SCRIPT     |
  |   ====                    ======                   ===========     |
  |                                                                     |
  |  +---------------+    +------------------+     +-----------------+ |
  |  | Security.evtx |    | ALL 1,000,000    |     | Where-Object    | |
  |  | (1 million    |--->| events loaded    |---->| { $_.Id -eq     | |
  |  |  events)      |    | into memory      |     |   4625 }        | |
  |  +---------------+    +------------------+     +-----------------+ |
  |                               |                        |           |
  |                               v                        v           |
  |                        Memory: 2GB+              Result: 50 events |
  |                        Time: 15 minutes          (0.005% matched)  |
  +=====================================================================+

                        OPTIMIZED QUERY (XPath/FilterHashtable)
  +====================================================================+
  |                                                                     |
  |   DISK                    KERNEL                   YOUR SCRIPT     |
  |   ====                    ======                   ===========     |
  |                                                                     |
  |  +---------------+    +------------------+     +-----------------+ |
  |  | Security.evtx |    | Windows Event    |     | Receive only    | |
  |  | (1 million    |--->| Log Service      |---->| 50 matching     | |
  |  |  events)      |    | (native filter)  |     | events          | |
  |  +---------------+    +------------------+     +-----------------+ |
  |                               |                        |           |
  |        Index-based lookup     |                        v           |
  |        (EventID indexed)      |                 Memory: 5MB        |
  |                               v                 Time: 2 seconds    |
  |                        Only reads matching records!                |
  +=====================================================================+

The slow approach (NEVER use in production):

# SLOW: Gets ALL events, then filters in PowerShell
Get-WinEvent -LogName Security |
    Where-Object { $_.Id -eq 4625 -and $_.TimeCreated -gt (Get-Date).AddHours(-1) }
# May read millions of events from disk!

The optimized approach using XPath:

# FAST: Filters at the source, only returns matching events
$query = "*[System[(EventID=4625) and TimeCreated[timediff(@SystemTime) <= 3600000]]]"
Get-WinEvent -LogName Security -FilterXPath $query
# Only reads relevant events

XPath syntax basics:

*[System[
    (EventID=4625)                           <!-- Event ID filter -->
    and
    (EventID=4624)                           <!-- Multiple IDs: or -->
    and
    TimeCreated[@SystemTime >= '2025-12-26T00:00:00.000Z']  <!-- After date -->
    and
    TimeCreated[timediff(@SystemTime) <= 86400000]         <!-- Last 24h in ms -->
]]
and
*[EventData[
    Data[@Name='TargetUserName']='jsmith'    <!-- Specific field value -->
]]

FilterHashtable alternative (easier but less flexible):

$filter = @{
    LogName = 'Security'
    ID = 4625
    StartTime = (Get-Date).AddHours(-1)
}
Get-WinEvent -FilterHashtable $filter

FilterHashtable vs FilterXPath Comparison

Feature FilterHashtable FilterXPath
Ease of use Easy - PowerShell syntax Hard - XPath syntax
Performance Good Excellent
Time filtering StartTime/EndTime only Any time expression
EventData filtering Data=@{…} (limited) Full XPath predicates
Debugging Clear error messages Cryptic errors

Event Correlation: Connecting the Dots

Book Reference: Learn PowerShell in a Month of Lunches by Travis Plunk and Don Jones, Chapter 22: “Background Jobs and Parallel Processing” - helpful for building parallel event analysis.

The Correlation Challenge

                        THE SIGNAL VS NOISE PROBLEM
  +====================================================================+
  |                                                                     |
  |   RAW EVENTS (1 day)        AFTER CORRELATION        ACTIONABLE    |
  |   ==================        =================        ==========    |
  |                                                                     |
  |   +----------------+        +----------------+      +------------+ |
  |   | 4624: 5,000    |        | Brute Force    |      | CRITICAL   | |
  |   | 4625: 200      |  --->  | Attempts: 3    | ---> | ALERT!     | |
  |   | 4672: 1,500    |        +----------------+      +------------+ |
  |   | 4634: 4,800    |        | Priv Escalation|                     |
  |   | 7034: 15       |        | Detected: 1    |      +------------+ |
  |   | 7036: 500      |        +----------------+      | Investigate| |
  |   +----------------+        | Service Crash  | ---> | 2 services | |
  |         |                   | Cluster: 5     |      +------------+ |
  |    12,015 events              9 findings            3 priorities   |
  +=====================================================================+

Correlation Strategies

                         CORRELATION TECHNIQUES
  +====================================================================+
  |                                                                     |
  |  1. TEMPORAL CORRELATION (events close in time)                    |
  |  ==============================================                    |
  |                                                                     |
  |  TIMELINE:                                                          |
  |  10:00:00  |--[4625]--|--[4625]--|--[4625]--|-...-|--[4625]--|     |
  |            |  fail1   |  fail2   |  fail3   |     |  fail50  |     |
  |            |<----------- 30 seconds ------------>|                 |
  |                                                                     |
  |  DETECTION: Count events within sliding time window                |
  |  THRESHOLD: >10 failed logins in 5 minutes from same source       |
  |                                                                     |
  +--------------------------------------------------------------------+
  |                                                                     |
  |  2. SEQUENTIAL CORRELATION (events in specific order)              |
  |  ====================================================              |
  |                                                                     |
  |  ATTACK CHAIN:                                                      |
  |  [4624 Login] ---> [4672 Privs] ---> [4688 Process] ---> [4634]   |
  |       |                  |                 |                |      |
  |       v                  v                 v                v      |
  |   User logs in    Gets debug priv   Runs mimikatz.exe   Logs off  |
  |                                                                     |
  |  DETECTION: Look for event sequence within time window             |
  |                                                                     |
  +--------------------------------------------------------------------+
  |                                                                     |
  |  3. STATISTICAL CORRELATION (deviation from baseline)              |
  |  ====================================================              |
  |                                                                     |
  |  NORMAL WEEK:  [===] [===] [===] [===] [===]  (avg: 50/day)       |
  |  THIS WEEK:    [===] [===] [==========] [===]  (spike: 200)       |
  |                                        ^                           |
  |                              ANOMALY DETECTED!                     |
  +=====================================================================+

Attack Pattern Examples

Example: Brute Force Attack Detection

RAW EVENTS:
===========
Time        EventID  User     IP              Status
10:00:01    4625     jsmith   192.168.1.105   0xC000006D (bad password)
10:00:02    4625     jsmith   192.168.1.105   0xC000006D
10:00:03    4625     jsmith   192.168.1.105   0xC000006D
...
10:00:30    4625     jsmith   192.168.1.105   0xC000006D

CORRELATION LOGIC:
==================
Group by: SourceIP
Count:    50 events
Window:   30 seconds

FINDING:
========
Type:     BRUTE_FORCE
Severity: CRITICAL
Source:   192.168.1.105
Target:   jsmith@CORP
Attempts: 50 in 30 seconds
Action:   Block IP, notify SOC

Example: Privilege Escalation Detection

RAW EVENTS:
===========
Time        EventID  Detail
10:00:00    4624     jsmith logs in (LogonType 3, network)
10:00:01    4672     jsmith assigned SeDebugPrivilege
10:00:05    4688     jsmith runs mimikatz.exe (suspicious!)

FINDING:
========
Type:     PRIVILEGE_ESCALATION
Severity: CRITICAL
User:     jsmith
Evidence: Normal user obtained debug privilege, ran known attack tool
Action:   Disable account, forensic investigation

Questions to Guide Your Design

Before writing code, answer these questions:

Query Strategy Questions

  1. What’s your expected log volume? (hundreds, thousands, or millions of events per day)
  2. Should you query all events once or make multiple targeted queries?
  3. How will you handle machines with limited audit policies?

Detection Logic Questions

  1. What constitutes a “brute force” attempt? (10 failures in 5 minutes? 50 in 1 minute?)
  2. How do you distinguish password spraying from brute force?
  3. What privileges indicate potential escalation?

Performance Questions

  1. What’s your target analysis time for 24 hours of logs?
  2. How much memory can your analyzer use?
  3. Can you parallelize analysis across multiple logs?

Output Questions

  1. Who is the target audience for your reports? (SOC analyst, manager, automated system)
  2. What severity thresholds trigger email alerts?

Thinking Exercise

Work through this scenario on paper before implementing:

Scenario: The Mystery Attack

You’re given these events from a 1-hour window:

Time        EventID  Details
09:00:00    4624     user: svc_backup, type: 5 (service), IP: -
09:15:00    4625     user: admin, type: 10 (RDP), IP: 10.0.0.50
09:15:02    4625     user: admin, type: 10 (RDP), IP: 10.0.0.50
09:15:04    4625     user: admin, type: 10 (RDP), IP: 10.0.0.50
09:15:10    4624     user: admin, type: 10 (RDP), IP: 10.0.0.50
09:16:00    4672     user: admin, privs: SeDebugPrivilege
09:17:00    4688     user: admin, process: cmd.exe
09:17:30    4688     user: admin, process: net.exe user hacker P@ssw0rd /add
09:18:00    4720     user: hacker created by admin
09:45:00    4625     user: guest, type: 3 (network), IP: 192.168.1.200
09:46:00    4625     user: test, type: 3 (network), IP: 192.168.1.200
09:55:00    7034     service: WinDefend crashed

Questions:

  1. How many distinct “attack scenarios” are represented?
  2. What type of attack is happening at 09:15?
  3. What’s suspicious about the 09:17:30 event?
  4. Is the 09:45-09:46 activity related to the earlier attack?
  5. What findings would your analyzer produce?

The Interview Questions They’ll Ask

Technical Questions

  1. “What’s the difference between Get-WinEvent and Get-EventLog?”
    • Get-WinEvent is newer, supports .evtx format, uses FilterHashtable/FilterXPath
    • Get-WinEvent is significantly faster for large logs
  2. “How would you query a million events efficiently?”
    • Use FilterHashtable or FilterXPath to filter at the source
    • Never use Where-Object on all events
  3. “Explain how you’d detect a brute force attack.”
    • Query 4625 events, group by source IP, count within time window, flag if exceeds threshold
  4. “What’s XPath and why use it for event logs?”
    • XML query language native to Windows Event Log, filters at kernel level

Design Questions

  1. “How would you scale this to 100 servers?”
    • Windows Event Forwarding, parallel queries, database backend
  2. “How do you minimize false positives?”
    • Baseline normal behavior, whitelist service accounts, context-aware thresholds

Project Specification

Functional Requirements

ID Requirement Priority
F1 Query specific event IDs efficiently Must Have
F2 Filter by time range Must Have
F3 Parse event data fields Must Have
F4 Detect failed login patterns Must Have
F5 Detect privilege escalation Must Have
F6 Group events by source/user/IP Should Have
F7 Generate summary reports Should Have
F8 Email alerts for critical findings Should Have
F9 Export to various formats (CSV, HTML) Should Have
F10 Cross-log correlation Nice to Have
F11 Remote server log analysis Nice to Have

Non-Functional Requirements

ID Requirement
NF1 Analyze 24 hours of logs in < 60 seconds
NF2 Memory usage < 500MB for large analyses
NF3 Works with default Windows audit policies
NF4 Graceful handling of access denied

Solution Architecture

Data Flow

[Configure Analysis Parameters]
        |
        v
Build XPath/FilterHashtable Query
        |
        v
Get-WinEvent with Optimized Filter
        |
        +-- Security Log: 4624, 4625, 4672, etc.
        +-- System Log: 7034, 7035, 7036
        +-- Application Log: Errors
        |
        v
Parse Event Data into Structured Objects
        |
        v
Apply Correlation Rules
        |
        +-- Group by User/IP/Time
        +-- Calculate frequencies
        +-- Detect anomalies
        |
        v
Generate Findings
        |
        +-- Brute force attempts
        +-- Privilege escalations
        +-- Service failures
        |
        v
Output (Report / Alert / Export)

Key Data Structures

# Parsed security event
$SecurityEvent = [PSCustomObject]@{
    TimeCreated = [DateTime]
    EventId = [int]
    EventType = [string]    # "FailedLogin", "SuccessLogin", etc.
    Severity = [string]     # "Info", "Warning", "Critical"
    Account = [string]
    Domain = [string]
    SourceIP = [string]
    LogonType = [int]
    Status = [string]
    Details = [hashtable]
}

# Correlation finding
$Finding = [PSCustomObject]@{
    Type = "BruteForce"
    Severity = "Critical"
    TimeRange = @{ Start = [DateTime]; End = [DateTime] }
    AffectedAccount = "jsmith"
    SourceIP = "192.168.1.105"
    EventCount = 47
    Description = "47 failed login attempts in 30 seconds"
    RecommendedAction = "Block IP, investigate account"
    RelatedEvents = @()
}

Implementation Guide

Phase 1: Basic Event Querying (2-3 hours)

Goal: Query and display security events.

Steps:

  1. Use Get-WinEvent with FilterHashtable
  2. Parse common security event IDs
  3. Extract relevant fields from EventData
  4. Display in readable format

Phase 2: Event Parsing (2-3 hours)

Goal: Transform raw events into structured objects.

Steps:

  1. Create parsing functions for each event type
  2. Handle XML EventData extraction
  3. Normalize field names across events
  4. Add severity classification

Phase 3: Failed Login Analysis (2-3 hours)

Goal: Detect and report authentication failures.

Steps:

  1. Query 4625 events
  2. Group by account and source IP
  3. Calculate attempt rates
  4. Flag potential brute force

Phase 4: Correlation Engine (3-4 hours)

Goal: Link related events into findings.

Steps:

  1. Build time-window correlation
  2. Detect privilege escalation patterns
  3. Track service failures
  4. Generate finding objects

Phase 5: Reporting (2-3 hours)

Goal: Generate actionable reports.

Steps:

  1. Summary statistics
  2. Critical findings section
  3. HTML report generation
  4. Email delivery option

Phase 6: Performance Optimization (2-3 hours)

Goal: Handle large log volumes.

Steps:

  1. Optimize XPath queries
  2. Stream processing for memory efficiency
  3. Parallel analysis for multiple servers
  4. Caching for repeated analyses

Hints in Layers

Use these hints progressively - try to solve problems yourself before looking at the next layer.

Layer 1: Conceptual Hints

Hint: How to structure your query layer

Think of queries in three tiers:

  1. Fast path: FilterHashtable for simple ID + time queries
  2. Flexible path: XPath for complex conditions
  3. Fallback path: Where-Object only for post-processing
Hint: How to parse EventData efficiently

EventData is XML inside the event. Use $event.ToXml() to get it, then parse with [xml] cast. Store field mappings in a hashtable keyed by EventID.

Hint: How to implement sliding window detection

Sort events by time, then iterate with two pointers. For each event, find all events within the window. Use Group-Object first to reduce the search space.

Layer 2: Code Structure Hints

Hint: Module structure
SecurityEventAnalyzer/
  SecurityEventAnalyzer.psm1
  Public/
    Get-SecurityEvents.ps1
    Find-BruteForceAttempts.ps1
  Private/
    ConvertTo-SecurityEvent.ps1
Hint: Event type mapping
$EventTypeMap = @{
    4624 = @{ Type = 'SuccessLogin'; Severity = 'Info' }
    4625 = @{ Type = 'FailedLogin'; Severity = 'Warning' }
    4672 = @{ Type = 'SpecialPrivilege'; Severity = 'Warning' }
    4740 = @{ Type = 'AccountLocked'; Severity = 'Critical' }
}

Code Hints

Hint 1: Efficient Event Querying

function Get-SecurityEvents {
    [CmdletBinding()]
    param(
        [Parameter()]
        [int[]]$EventId,

        [Parameter()]
        [DateTime]$StartTime = (Get-Date).AddHours(-24),

        [Parameter()]
        [DateTime]$EndTime = (Get-Date),

        [Parameter()]
        [int]$MaxEvents = 10000
    )

    $filter = @{
        LogName = 'Security'
        StartTime = $StartTime
        EndTime = $EndTime
    }

    if ($EventId) {
        $filter['ID'] = $EventId
    }

    Write-Verbose "Querying Security log from $StartTime to $EndTime"

    try {
        Get-WinEvent -FilterHashtable $filter -MaxEvents $MaxEvents -ErrorAction Stop
    }
    catch [Exception] {
        if ($_.Exception.Message -like "*No events were found*") {
            Write-Verbose "No matching events found"
            return @()
        }
        throw
    }
}

# Usage:
$failedLogins = Get-SecurityEvents -EventId 4625 -StartTime (Get-Date).AddHours(-1)

Hint 2: Event Data Parsing

function ConvertTo-SecurityEvent {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Diagnostics.Eventing.Reader.EventLogRecord]$Event
    )

    process {
        $xml = [xml]$Event.ToXml()
        $eventData = @{}

        $xml.Event.EventData.Data | ForEach-Object {
            $eventData[$_.Name] = $_.'#text'
        }

        $eventType = switch ($Event.Id) {
            4624 { 'SuccessLogin' }
            4625 { 'FailedLogin' }
            4634 { 'Logoff' }
            4648 { 'ExplicitCredential' }
            4672 { 'SpecialPrivileges' }
            4720 { 'AccountCreated' }
            4726 { 'AccountDeleted' }
            4740 { 'AccountLocked' }
            default { 'Unknown' }
        }

        $severity = switch ($Event.Id) {
            4625 { 'Warning' }
            4740 { 'Critical' }
            4672 { 'Warning' }
            4726 { 'Warning' }
            default { 'Info' }
        }

        [PSCustomObject]@{
            TimeCreated = $Event.TimeCreated
            EventId = $Event.Id
            EventType = $eventType
            Severity = $severity
            Account = $eventData['TargetUserName']
            Domain = $eventData['TargetDomainName']
            SourceIP = $eventData['IpAddress']
            LogonType = [int]$eventData['LogonType']
            Status = $eventData['Status']
            SubStatus = $eventData['SubStatus']
            Workstation = $eventData['WorkstationName']
            ProcessName = $eventData['ProcessName']
            Details = $eventData
        }
    }
}

# Usage:
$events = Get-SecurityEvents -EventId 4625 | ConvertTo-SecurityEvent

Hint 3: Brute Force Detection

function Find-BruteForceAttempts {
    [CmdletBinding()]
    param(
        [Parameter()]
        [int]$Hours = 24,

        [Parameter()]
        [int]$ThresholdCount = 10,

        [Parameter()]
        [int]$TimeWindowMinutes = 5
    )

    $failedLogins = Get-SecurityEvents -EventId 4625 -StartTime (Get-Date).AddHours(-$Hours) |
        ConvertTo-SecurityEvent

    Write-Verbose "Found $($failedLogins.Count) failed login events"

    $byIP = $failedLogins | Where-Object { $_.SourceIP } | Group-Object SourceIP

    $findings = foreach ($group in $byIP) {
        $ip = $group.Name
        $events = $group.Group | Sort-Object TimeCreated

        for ($i = 0; $i -lt $events.Count; $i++) {
            $windowStart = $events[$i].TimeCreated
            $windowEnd = $windowStart.AddMinutes($TimeWindowMinutes)

            $windowEvents = $events | Where-Object {
                $_.TimeCreated -ge $windowStart -and $_.TimeCreated -le $windowEnd
            }

            if ($windowEvents.Count -ge $ThresholdCount) {
                $accounts = $windowEvents | Select-Object -ExpandProperty Account -Unique

                [PSCustomObject]@{
                    Type = 'BruteForce'
                    Severity = 'Critical'
                    SourceIP = $ip
                    StartTime = $windowStart
                    EndTime = ($windowEvents | Select-Object -Last 1).TimeCreated
                    AttemptCount = $windowEvents.Count
                    TargetAccounts = $accounts -join ', '
                    Description = "$($windowEvents.Count) failed logins from $ip in $TimeWindowMinutes minutes"
                    RecommendedAction = "Block IP $ip, investigate targeted accounts"
                }

                $i = $events.IndexOf(($windowEvents | Select-Object -Last 1))
            }
        }
    }

    return $findings | Sort-Object AttemptCount -Descending
}

Hint 4: Privilege Escalation Detection

function Find-PrivilegeEscalation {
    [CmdletBinding()]
    param(
        [int]$Hours = 24
    )

    $startTime = (Get-Date).AddHours(-$Hours)

    $privEvents = Get-SecurityEvents -EventId 4672 -StartTime $startTime |
        ConvertTo-SecurityEvent

    $sensitivePrivs = @(
        'SeDebugPrivilege',
        'SeTakeOwnershipPrivilege',
        'SeLoadDriverPrivilege',
        'SeBackupPrivilege',
        'SeRestorePrivilege',
        'SeImpersonatePrivilege'
    )

    $logins = Get-SecurityEvents -EventId 4624 -StartTime $startTime |
        ConvertTo-SecurityEvent |
        Group-Object Account

    $normalUsers = $logins | Where-Object {
        $_.Name -notmatch '^(SYSTEM|LOCAL SERVICE|NETWORK SERVICE|\$)' -and
        $_.Name -notmatch 'admin'
    } | Select-Object -ExpandProperty Name

    $findings = foreach ($event in $privEvents) {
        if ($event.Account -in $normalUsers) {
            $privileges = $event.Details['PrivilegeList'] -split '\s*,\s*'
            $sensitiveGranted = $privileges | Where-Object { $_ -in $sensitivePrivs }

            if ($sensitiveGranted) {
                [PSCustomObject]@{
                    Type = 'PrivilegeEscalation'
                    Severity = 'Critical'
                    TimeCreated = $event.TimeCreated
                    Account = $event.Account
                    Domain = $event.Domain
                    PrivilegesGranted = $sensitiveGranted -join ', '
                    Description = "Normal user $($event.Account) received sensitive privileges"
                    RecommendedAction = "Investigate account activity, verify if authorized"
                }
            }
        }
    }

    return $findings
}

Hint 5: Report Generation

function New-SecurityReport {
    [CmdletBinding()]
    param(
        [DateTime]$Date = (Get-Date).Date.AddDays(-1),
        [string]$OutputPath = ".",
        [string]$EmailTo
    )

    $reportDate = $Date.ToString("yyyy-MM-dd")
    $startTime = $Date
    $endTime = $Date.AddDays(1)

    Write-Verbose "Generating security report for $reportDate"

    $bruteForce = Find-BruteForceAttempts -Hours 24
    $escalations = Find-PrivilegeEscalation -Hours 24

    $allEvents = Get-SecurityEvents -StartTime $startTime -EndTime $endTime
    $failedLogins = ($allEvents | Where-Object Id -eq 4625).Count
    $successLogins = ($allEvents | Where-Object Id -eq 4624).Count

    $css = @"
<style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    h1 { color: #333; border-bottom: 2px solid #c00; }
    table { border-collapse: collapse; width: 100%; margin: 20px 0; }
    th { background: #333; color: white; padding: 10px; text-align: left; }
    td { border: 1px solid #ddd; padding: 8px; }
    .critical { background: #f8d7da; color: #721c24; font-weight: bold; }
    .stat-box { display: inline-block; padding: 20px; margin: 10px; background: #f5f5f5; }
    .stat-number { font-size: 2em; font-weight: bold; }
</style>
"@

    $html = @"
<!DOCTYPE html>
<html>
<head><title>Security Report - $reportDate</title>$css</head>
<body>
    <h1>Security Report - $reportDate</h1>
    <div class="stat-box"><div class="stat-number">$($allEvents.Count)</div><div>Total Events</div></div>
    <div class="stat-box"><div class="stat-number">$successLogins</div><div>Successful Logins</div></div>
    <div class="stat-box"><div class="stat-number">$failedLogins</div><div>Failed Logins</div></div>
    <h2>Critical Findings</h2>
"@

    if ($bruteForce) {
        $html += "<h3>Brute Force Attacks ($($bruteForce.Count))</h3>"
        $html += $bruteForce | Select-Object SourceIP, AttemptCount, TargetAccounts | ConvertTo-Html -Fragment
    }

    if ($escalations) {
        $html += "<h3>Privilege Escalations ($($escalations.Count))</h3>"
        $html += $escalations | Select-Object TimeCreated, Account, PrivilegesGranted | ConvertTo-Html -Fragment
    }

    $html += "<hr><p><em>Generated: $(Get-Date)</em></p></body></html>"

    $reportPath = Join-Path $OutputPath "SecurityReport_$reportDate.html"
    $html | Out-File $reportPath -Encoding UTF8

    Write-Output "Report saved to: $reportPath"

    return [PSCustomObject]@{
        ReportPath = $reportPath
        TotalEvents = $allEvents.Count
        CriticalFindings = ($bruteForce.Count + $escalations.Count)
    }
}

Testing Strategy

Unit Tests

Test Steps Expected
Query by ID Get events with specific ID Only matching IDs returned
Parse 4625 Parse failed login event All fields extracted
Time filter Query with StartTime Only events in range
Empty results Query for non-existent events Empty array, no error

Integration Tests

Test Steps Expected
Full analysis Run 24-hour analysis Report generated
Brute force detect Generate 20 failed logins Finding created
HTML report Generate report Valid HTML opens in browser

Test Scenario Creation

# Simulate brute force - run on test machine
1..50 | ForEach-Object {
    $cred = New-Object PSCredential "testuser", (ConvertTo-SecureString "wrongpassword$_" -AsPlainText -Force)
    try { Start-Process notepad -Credential $cred -ErrorAction Stop }
    catch { }  # Expected to fail
}
# This creates 50 4625 events in Security log

Common Pitfalls and Debugging Tips

Pitfall 1: Access Denied to Security Log

Symptoms: Error “Access is denied”

Solutions:

# Check if running as admin
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Write-Warning "Run PowerShell as Administrator to access Security log"
}

# Add user to Event Log Readers (run as admin)
Add-LocalGroupMember -Group "Event Log Readers" -Member "DOMAIN\username"

Pitfall 2: Slow Queries on Large Logs

Symptoms: Queries take minutes instead of seconds

Debugging:

# Measure query time
Measure-Command {
    $events = Get-WinEvent -LogName Security -FilterHashtable @{ID=4625} -MaxEvents 100
}

# Compare with naive approach - this should be 10-100x slower
Measure-Command {
    $events = Get-WinEvent -LogName Security -MaxEvents 1000 | Where-Object { $_.Id -eq 4625 }
}

Pitfall 3: Missing EventData Fields

Symptoms: Account, SourceIP, or other fields are null

Solutions:

# Always check if field exists
$account = if ($eventData.ContainsKey('TargetUserName')) {
    $eventData['TargetUserName']
} else {
    $eventData['SubjectUserName']  # Fallback
}

# Debug by inspecting raw XML
$event = Get-WinEvent -LogName Security -MaxEvents 1 -FilterHashtable @{ID=4625}
$event.ToXml() | Out-File debug.xml

Pitfall 4: Audit Policy Not Enabled

Symptoms: Expected events don’t exist in log

Debugging:

# Check current audit policy
auditpol /get /category:*

# Check if specific events are being generated
Get-WinEvent -LogName Security -MaxEvents 100 | Group-Object Id | Sort-Object Count -Descending

# Enable more auditing (requires admin)
auditpol /set /subcategory:"Logon" /success:enable /failure:enable

Pitfall 5: Memory Exhaustion

Symptoms: PowerShell crashes or becomes unresponsive

Solutions:

# Process in batches
$batchSize = 1000
$skip = 0
do {
    $events = Get-WinEvent -LogName Security -MaxEvents $batchSize -FilterHashtable @{StartTime=(Get-Date).AddHours(-24)} |
        Select-Object -Skip $skip -First $batchSize

    # Process this batch
    $events | ConvertTo-SecurityEvent | ForEach-Object { }

    $skip += $batchSize
} while ($events.Count -eq $batchSize)

# Force garbage collection
[GC]::Collect()

Extensions and Challenges

Easy Extensions (1-2 hours each)

  1. Configuration File - Move thresholds to JSON config
  2. CSV Export - Export findings for SIEM import
  3. Console Colors - Color-coded severity output

Medium Extensions (3-5 hours each)

  1. Password Spraying Detection - One-password-many-accounts pattern
  2. Service Account Baseline - Detect new service account activity
  3. Scheduled Analysis - Run as Windows scheduled task

Advanced Extensions (8+ hours each)

  1. Multi-Server Analysis - Query logs from multiple servers
  2. Machine Learning Anomaly Detection - Statistical baseline
  3. Real-Time Monitoring - Subscribe to event log changes

Books That Will Help

Topic Book Chapter/Section Why This Helps
Windows Security Events Windows Security Internals by James Forshaw Part 2: “Security Auditing” Deep understanding of how Windows generates security events
PowerShell Event Logs PowerShell in Depth by Don Jones et al. Chapter 21: “Working with Events” Covers Get-WinEvent, FilterHashtable, XPath queries
PowerShell Fundamentals Learn PowerShell in a Month of Lunches by Travis Plunk Chapters 20-22 Foundation for structuring your analyzer as a module
Security Operations The Practice of Network Security Monitoring by Richard Bejtlich Part II Security monitoring mindset: baselines, anomalies, correlation
Threat Detection Crafting the InfoSec Playbook by Jeff Bollinger Chapter 5: “Developing Indicators” How to develop detection rules and manage false positives
Attack Patterns The Hacker Playbook 3 by Peter Kim Various attack chapters Understand attacks to detect them

Self-Assessment Checklist

Conceptual Understanding

  • I can explain what an .evtx file contains and how Windows generates security events
  • I understand the difference between FilterHashtable and XPath and when to use each
  • I can describe 5+ critical security event IDs and what they indicate
  • I understand logon types and their security implications
  • I can explain temporal correlation and how it detects brute force attacks
  • I understand why Where-Object is slow for event log filtering

Implementation Quality

  • Queries complete in reasonable time (<60 seconds for 24h of logs)
  • All event data fields are properly extracted
  • Brute force detection works correctly with configurable thresholds
  • Privilege escalation detection identifies sensitive privileges
  • HTML report is valid and readable
  • No false positives for service accounts

Code Quality

  • Module structure is clean with Public/Private separation
  • Error handling is comprehensive
  • Verbose output aids debugging
  • Functions are documented with help comments

Final Verification Questions

  1. What’s the difference between event ID 4624 and 4625?
  2. Why is logon type 8 (NetworkCleartext) high risk?
  3. What does a Status of 0xC000006D mean in a 4625 event?
  4. How would you detect password spraying vs brute force?
  5. What privilege indicates potential credential dumping?

Resources

Official Documentation

Security References


Start by understanding the Windows Event Log architecture, then build your query layer, add detection logic, and finally implement reporting. By the end you’ll have a powerful security analysis tool.