Skip to content

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.mscComputer ConfigurationAdministrative TemplatesWindows ComponentsWindows 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.

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.

Next

Privacy: Diagnostic Data and Telemetry