PowerShell Script Block Logging

PowerShell Script Block Logging

One-liner: A Windows logging feature (Event ID 4104) that records the full content of PowerShell scripts and commands as they execute, even if obfuscated or encoded.

🎯 What Is It?

PowerShell Script Block Logging is a security auditing feature that captures the actual code executed by PowerShell, regardless of how it was invoked. This is critical for threat hunting because attackers frequently use PowerShell for:

Key Benefit: Even heavily obfuscated or Base64-encoded scripts are logged in their deobfuscated form after PowerShell interprets them.

🔬 How It Works

What Gets Logged

Event ID: 4104
Channel: Microsoft-Windows-PowerShell/Operational
Field Description
ScriptBlockText The actual PowerShell code executed
ScriptBlockId Unique identifier for the script block
Path Script file path (if from file)
MessageNumber Part number if script spans multiple events
MessageTotal Total parts for large scripts

Example Log Entry

<Event>
  <System>
    <EventID>4104</EventID>
    <Channel>Microsoft-Windows-PowerShell/Operational</Channel>
  </System>
  <EventData>
    <Data Name="ScriptBlockText">
      Invoke-Mimikatz -DumpCreds
    </Data>
    <Data Name="Path">C:\Users\attacker\evil.ps1</Data>
  </EventData>
</Event>

Why It's Powerful

Attacker runs:
powershell -enc SQBuAHYAbwBrAGUALQBNAGkAbQBpAGsAYQB0AHoA...

Script Block Logging captures:
Invoke-Mimikatz -DumpCreds

The decoded command is logged, not the encoded blob!

⚙️ Enabling Script Block Logging

Via Group Policy

Computer Configuration 
  → Administrative Templates 
    → Windows Components 
      → Windows PowerShell 
        → Turn on PowerShell Script Block Logging

Via Registry

# Enable Script Block Logging
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Name "EnableScriptBlockLogging" -Value 1

PowerShell v5+ Automatic Logging

PowerShell 5.0+ automatically logs "suspicious" script blocks even without explicit configuration when:

🕵️ Detection & Hunting

Suspicious Strings to Hunt

From the THM Threat Hunting Foothold room:

String Indicates
Invoke-Expression / IEX Dynamic code execution
-enc / -EncodedCommand Base64 encoded commands
WebRequest / DownloadString Downloading remote payloads
Invoke-Mimikatz Credential theft
Invoke-Empire Empire C2 framework
bypass Execution policy bypass
-nop / -noprofile Stealth execution
FromBase64String Decoding payloads

KQL Query Example

# Hunt unusual PowerShell execution
host.name: WKSTN-* AND winlog.event_id: 4104

Filtering Noise

# Exclude common benign patterns
host.name: WKSTN-* AND winlog.event_id: 4104 
  AND NOT powershell.file.script_block_text: "Set-StrictMode"

Key Fields to Add as Columns

Log Type Event ID What It Captures
Script Block Logging 4104 Actual script content (deobfuscated)
Module Logging 4103 Pipeline execution details
Transcription N/A Full session transcript to file
Command History N/A ConsoleHost_history.txt file

🎤 Interview Angles

Common Questions

STAR Story

Situation: Alerts were triggering on encoded PowerShell commands but we couldn't determine their actual intent.
Task: Improve visibility into PowerShell execution for threat hunting.
Action: Enabled Script Block Logging via GPO across all endpoints. Created detection rules for known malicious patterns (Invoke-Mimikatz, Empire signatures). Built a Kibana dashboard filtering Event ID 4104 for suspicious keywords.
Result: Detected Empire C2 agent execution within 24 hours of deployment. The deobfuscated logs showed Invoke-Empire clearly despite the encoded command line.

✅ Best Practices

❌ Common Misconceptions

📚 References