src/Public/Collect/Invoke-FotonEventLogCollection.ps1
|
# Invoke-FotonEventLogCollection # Live facts only. Called from Invoke-FotonTriage when that exists. # Endpoint collect. Rebuild dist/Foton-Collect.ps1 after edits. function Invoke-FotonEventLogCollection { $eventLogDays = $script:FotonSession.EventLogDays $maxEventsPerLog = $script:FotonSession.MaxEventsPerLog Write-FotonStatus "Collecting event logs (last $eventLogDays days, max $maxEventsPerLog per query)" 'Step' $start = (Get-Date).AddDays(-1 * $eventLogDays) $queries = @( @{ Name = 'security_logon_events'; Log = 'Security'; Ids = @(4624, 4625, 4634, 4647, 4648, 4672, 4778, 4779); Desc = 'Logon success/failure, explicit credential use, privilege assignment' }, @{ Name = 'security_account_management'; Log = 'Security'; Ids = @(4720, 4722, 4723, 4724, 4725, 4726, 4727, 4728, 4729, 4732, 4733, 4738, 4740, 4756, 4757, 4767, 4781); Desc = 'Account creation, modification, group membership changes' }, @{ Name = 'security_process_creation'; Log = 'Security'; Ids = @(4688, 4689); Desc = 'Process creation/termination (requires audit policy enabled)' }, @{ Name = 'security_policy_and_log_changes'; Log = 'Security'; Ids = @(1102, 4719, 4739, 4907, 4912); Desc = 'Audit log cleared and audit policy changes' }, @{ Name = 'security_object_and_share_access'; Log = 'Security'; Ids = @(5140, 5142, 5143, 5144, 5145, 4663, 4656); Desc = 'Share and object access events' }, @{ Name = 'security_scheduled_task_and_service'; Log = 'Security'; Ids = @(4697, 4698, 4699, 4700, 4701, 4702); Desc = 'Service installation and scheduled task changes' }, @{ Name = 'security_kerberos_ntlm'; Log = 'Security'; Ids = @(4768, 4769, 4771, 4776); Desc = 'Kerberos/NTLM authentication events' }, @{ Name = 'system_service_and_driver'; Log = 'System'; Ids = @(7045, 7040, 7034, 7031, 7036, 219, 41, 1001); Desc = 'Service install/state change, driver load, unexpected shutdown' }, @{ Name = 'system_log_and_boot'; Log = 'System'; Ids = @(104, 6005, 6006, 6008, 6013, 1074); Desc = 'Log clearing, boot, shutdown events' }, @{ Name = 'powershell_operational'; Log = 'Microsoft-Windows-PowerShell/Operational'; Ids = @(4103, 4104, 53504); Desc = 'PowerShell script block and module logging' }, @{ Name = 'powershell_classic'; Log = 'Windows PowerShell'; Ids = @(400, 403, 600, 800); Desc = 'Classic PowerShell engine lifecycle and provider events' }, @{ Name = 'defender_operational'; Log = 'Microsoft-Windows-Windows Defender/Operational'; Ids = @(1006, 1007, 1008, 1009, 1015, 1116, 1117, 1118, 1119, 5001, 5004, 5007, 5010, 5012); Desc = 'Defender detections, actions, and configuration changes' }, @{ Name = 'task_scheduler_operational'; Log = 'Microsoft-Windows-TaskScheduler/Operational'; Ids = @(106, 129, 140, 141, 200, 201); Desc = 'Task registration, deletion, and execution' }, @{ Name = 'terminal_services_local'; Log = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'; Ids = @(21, 22, 23, 24, 25, 39, 40); Desc = 'RDP session connect/disconnect/reconnect' }, @{ Name = 'terminal_services_remote'; Log = 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational'; Ids = @(1149, 1148); Desc = 'RDP authentication attempts with source IP' }, @{ Name = 'wmi_activity'; Log = 'Microsoft-Windows-WMI-Activity/Operational'; Ids = @(5857, 5858, 5859, 5860, 5861); Desc = 'WMI provider and permanent subscription activity' }, @{ Name = 'windows_firewall'; Log = 'Microsoft-Windows-Windows Firewall With Advanced Security/Firewall'; Ids = @(2003, 2004, 2005, 2006, 2033); Desc = 'Firewall rule and profile changes' }, @{ Name = 'bits_client'; Log = 'Microsoft-Windows-Bits-Client/Operational'; Ids = @(3, 4, 59, 60); Desc = 'BITS transfer jobs (download activity)' }, @{ Name = 'app_crashes'; Log = 'Application'; Ids = @(1000, 1001, 1002); Desc = 'Application crash and hang reports' }, @{ Name = 'sysmon_operational'; Log = 'Microsoft-Windows-Sysmon/Operational'; Ids = @(1, 3, 6, 7, 8, 10, 11, 12, 13, 15, 22, 23, 25); Desc = 'Sysmon telemetry if Sysmon is deployed' } ) # GetNewClosure() copies *variables* into a private scope so each query keeps its own # $q, but it does not copy script functions. Capture the helpers here or the task # fails with "Get-FotonWinEvent is not recognized" (seen on Windows 11 / PS7). $fnGetEvents = ${function:Get-FotonWinEvent} $fnToRow = ${function:ConvertTo-FotonEventRow} foreach ($q in $queries) { $needAdmin = $false if ($q.Log -eq 'Security') { $needAdmin = $true } $query = $q $windowStart = $start $maxEv = $maxEventsPerLog Invoke-FotonCollection -Category 'EventLogs' -Name $q.Name -Format 'Csv' -Description $q.Desc -RequiresElevation:$needAdmin -Script { $events = & $fnGetEvents -LogName $query.Log -EventIds $query.Ids -StartTime $windowStart -MaxEvents $maxEv if (-not $events) { return @() } $rows = @() foreach ($e in $events) { $rows += (& $fnToRow -EventRecord $e -LogLabel $query.Log) } $rows | Sort-Object TimeCreatedUtc -Descending }.GetNewClosure() | Out-Null } Invoke-FotonCollection -Category 'EventLogs' -Name 'event_log_configuration' -Format 'Csv' -Description 'Log size, retention, and enablement (gap analysis)' -Script { $rows = @() $logs = @('Security', 'System', 'Application', 'Microsoft-Windows-PowerShell/Operational', 'Microsoft-Windows-TaskScheduler/Operational', 'Microsoft-Windows-Sysmon/Operational', 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational', 'Microsoft-Windows-Windows Defender/Operational', 'Microsoft-Windows-WMI-Activity/Operational') foreach ($l in $logs) { try { $cfg = Get-WinEvent -ListLog $l -ErrorAction Stop $rows += [PSCustomObject]@{ LogName = $cfg.LogName IsEnabled = $cfg.IsEnabled LogMode = [string]$cfg.LogMode MaximumSizeMB = [math]::Round($cfg.MaximumSizeInBytes / 1MB, 2) CurrentSizeMB = [math]::Round($cfg.FileSize / 1MB, 2) RecordCount = $cfg.RecordCount OldestRecordUtc = '' LogFilePath = $cfg.LogFilePath } } catch { $rows += [PSCustomObject]@{ LogName = $l; IsEnabled = 'NotPresent'; LogMode = ''; MaximumSizeMB = 0; CurrentSizeMB = 0; RecordCount = 0; OldestRecordUtc = ''; LogFilePath = '' } } } $rows } | Out-Null if (-not $script:FotonSession.SkipEvtxExport) { Write-FotonStatus 'Exporting raw EVTX files (time-filtered)' 'Step' $evtxDir = Join-Path $script:FotonSession.Dirs['EventLogs'] 'evtx' New-Item -Path $evtxDir -ItemType Directory -Force | Out-Null $ms = [int64]$eventLogDays * 86400000 $exportLogs = @('Security', 'System', 'Application', 'Microsoft-Windows-PowerShell/Operational', 'Microsoft-Windows-TaskScheduler/Operational', 'Microsoft-Windows-Windows Defender/Operational', 'Microsoft-Windows-Bits-Client/Operational', 'Microsoft-Windows-WMI-Activity/Operational', 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational', 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational', 'Microsoft-Windows-Sysmon/Operational', 'Microsoft-Windows-Windows Firewall With Advanced Security/Firewall') foreach ($log in $exportLogs) { if ((-not $script:FotonSession.IsElevated) -and ($log -eq 'Security')) { Write-FotonStatus "EVTX export skipped for Security (not elevated)" 'Info' continue } $safeName = ($log -replace '[\\/ ]', '_') + '.evtx' $dest = Join-Path $evtxDir $safeName try { $query = "*[System[TimeCreated[timediff(@SystemTime) <= $ms]]]" $null = & wevtutil.exe epl "$log" "$dest" "/q:$query" /ow:true 2>&1 if (Test-Path -LiteralPath $dest) { Write-FotonStatus "Exported $log" 'Good' } else { Write-FotonStatus "No EVTX export produced for $log" 'Info' } } catch { Write-FotonStatus "EVTX export failed for $log : $($_.Exception.Message)" 'Warn' } } } else { Write-FotonStatus 'EVTX export skipped by parameter' 'Info' } } |