Analyzing PowerShell Script Block Logging
When to Use
- When investigating security incidents that require analyzing powershell script block logging
- When building detection rules or threat hunting queries for this domain
- When SOC analysts need structured procedures for this analysis type
- When validating security monitoring coverage for related attack techniques
Detection Gaps & Validation
- If 4104 is disabled, you have nothing: Script Block Logging must be enabled via GPO (
Administrative Templates > Windows PowerShell > Turn on PowerShell Script Block Logging) or registryHKLM\...\PowerShell\ScriptBlockLogging\EnableScriptBlockLogging=1. Attackers runningpowershell -version 2downgrade to an engine with no 4104/AMSI at all. Hunt for 400/600 engine-start events showing v2 and for the absence of expected 4104 volume on a host. - AMSI bypass precedes the malicious block:
amsiInitFailed,[Ref].Assembly.GetType('...AmsiUtils'), and reflection/obfuscated variants neutralize scanning. Detect the bypass string itself in 4104, since the subsequent payload may log as benign or not at all. - Obfuscation defeats naive regex: tick marks (
I+EX), string-join/-fformat ops,[char]arrays, gzip+base64, andSecureStringhideIEX/DownloadString. Score on Shannon entropy and decoded content, not literal keywords. Reconstruct multi-part scripts byScriptBlockIdordered byMessageNumberbefore scoring — a split payload evades per-event matching. - Watch 4103 too: Module/pipeline logging (EID 4103) captures invocation detail 4104 misses; correlate both.
- Validate the rule fires: in a lab, run an
-EncodedCommanddownload cradle and an AMSI bypass, then confirm your parser flags both EID 4104 entries and that base64 decodes as UTF-16LE. FP tuning: legitimate admin tooling, Chocolatey, SCCM, and installers emit large/encoded blocks — baseline by signing cert and parent process before alerting.
Prerequisites
- Familiarity with security operations concepts and tools
- Access to a test or lab environment for safe execution
- Python 3.8+ with required dependencies installed
- Appropriate authorization for any testing activities
Instructions
- Install dependencies:
pip install python-evtx lxml - Collect PowerShell Operational logs:
Microsoft-Windows-PowerShell%4Operational.evtx - Parse Event ID 4104 entries using python-evtx to extract ScriptBlockText, ScriptBlockId, and MessageNumber/MessageTotal for multi-part script reconstruction.
- Apply detection heuristics:
- Base64-encoded commands (
-EncodedCommand,FromBase64String) - Download cradles (
DownloadString,DownloadFile,Invoke-WebRequest,Net.WebClient) - AMSI bypass patterns (
AmsiUtils,amsiInitFailed) - Obfuscation indicators (high entropy, tick-mark insertion, string concatenation)
- Base64-encoded commands (
- Generate a report with reconstructed scripts, risk scores, and MITRE ATT&CK mappings.
python scripts/agent.py --evtx-file /path/to/PowerShell-Operational.evtx --output ps_analysis.json
Examples
Detect Encoded Command Execution
import base64
if "-encodedcommand" in script_text.lower():
encoded = script_text.split()[-1]
decoded = base64.b64decode(encoded).decode("utf-16-le")
Reconstruct Multi-Block Script
Scripts split across multiple 4104 events share a ScriptBlockId. Concatenate blocks ordered by MessageNumber to recover the full script.