PowerShell Logging¶
Attackers use PowerShell because it's already on every Windows machine and leaves few traces. These two settings make it leave traces. They're Level 2 in the benchmark, but apply them on every image; nothing else answers "what did that script do" as well. Script block logging records every command PowerShell runs, including ones that were obfuscated or encoded, after they're decoded. Transcription writes a text record of every session.
These are the two most valuable settings on this page for forensics questions. Apply them on every image.
Where¶
gpedit.msc → Computer Configuration → Administrative Templates → Windows Components → Windows PowerShell.
The settings¶
| CIS | Setting | Set to | What it records |
|---|---|---|---|
| 18.10.88.1 | Turn on PowerShell Script Block Logging | Enabled | Every script block as it executes, decoded, to the Microsoft-Windows-PowerShell/Operational log, event ID 4104. Leave "Log script block invocation start / stop events" unticked; it's noisy. |
| 18.10.88.2 | Turn on PowerShell Transcription | Enabled | A text transcript of each session, including output, to the folder you specify. Set Transcript output directory to a folder standard users can't read, such as C:\PSTranscripts, and tick Include invocation headers. |
Apply¶
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v EnableScriptBlockLogging /t REG_DWORD /d 1 /f
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v EnableTranscripting /t REG_DWORD /d 1 /f
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v EnableInvocationHeader /t REG_DWORD /d 1 /f
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v OutputDirectory /t REG_SZ /d "C:\PSTranscripts" /f
mkdir C:\PSTranscripts
icacls C:\PSTranscripts /inheritance:r /grant:r "Administrators:(OI)(CI)F" "SYSTEM:(OI)(CI)F"
The icacls line makes the transcript folder readable only by administrators, so a user can't clean up after themselves.
Reading what it captured¶
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104} -MaxEvents 20 | Format-List TimeCreated, Message
Each 4104 event has the decoded script text. An encoded command (powershell -enc ...) shows up here in plain text.
Transcripts land in C:\PSTranscripts\<date>\PowerShell_transcript.<machine>.<id>.txt.
Related from Level 1¶
PowerShell Execution Policy controls what runs. This page controls what gets recorded. Both.
Verify¶
reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v EnableScriptBlockLogging
reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v EnableTranscripting
Both 1. Then open a new PowerShell window, run any command, and confirm a transcript file appeared.
Example¶
A forensics question asks what a scheduled task ran last night. The task's action was powershell -enc <base64>. With script block logging on, the 4104 event from that time contains the decoded command. Without it, you'd have to decode the base64 by hand and hope the task hadn't been changed since.