Understanding Script Block Logging (Part 3)

by Jun 26, 2018

This is part 3 of our mini-series covering PowerShell script block logging. By default, PowerShell logs only code that is considered security relevant. Today, we’ll enable verbose logging. With verbose logging turned on, any PowerShell code executed on your machine by any user will be logged.

To enable verbose mode, you need Administrator privileges. Here is a function that enables verbose logging:

function Enable-VerboseLogging { <# .SYNOPSIS Enables verbose script block logging. Requires Administrator privileges. .DESCRIPTION Turns script block logging on. Any code that is sent to PowerShell will be logged. .EXAMPLE Enable-VerboseLogging Enables script block logging. Administrator privileges required. #> $path = "Registry::HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" $exists = Test-Path -Path $path try { $ErrorActionPreference = 'Stop' if (!$exists) { $null = New-Item -Path $path -Force } Set-ItemProperty -Path $path -Name EnableScriptBlockLogging -Type DWord -Value 1 Set-ItemProperty -Path $path -Name EnableScriptBlockInvocationLogging -Type DWord -Value 1 } catch { Write-Warning "Administrator privileges required. Run this command from an elevated PowerShell." } } 

Once you run Enable-VerboseLogging, all PowerShell code will be written to the log. You can then use one of the approaches we introduced earlier to read the logged code, for example our function Get-LoggedCode:

function Get-LoggedCode { # read all raw events  $logInfo = @{ ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 } Get-WinEvent -FilterHashtable $logInfo | # take each raw set of data...  ForEach-Object { # create a new object and extract the interesting  # parts from the raw data to compose a "cooked"  # object with useful data  [PSCustomObject]@{ # when this was logged  Time = $_.TimeCreated # script code that was logged  Code = $_.Properties[2].Value # if code was split into multiple log entries,  # determine current and total part  PartCurrent = $_.Properties[0].Value PartTotal = $_.Properties[1].Value # if total part is 1, code is not fragmented  IsMultiPart = $_.Properties[1].Value -ne 1 # path of script file (this is empty for interactive  # commands)  Path = $_.Properties[4].Value # log level  # by default, only level "Warning" will be logged:  Level = $_.LevelDisplayName # user who executed the code (SID)  User = $_.UserId } } }  

Note that only changes to the logging settings require Administrator privileges. Any user can read the logged data.

If you’d like to disable verbose mode and return to the default settings, use this function:

function Get-LoggedCode { # read all raw events  $logInfo = @{ ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 } Get-WinEvent -FilterHashtable $logInfo | # take each raw set of data...  ForEach-Object { # create a new object and extract the interesting  # parts from the raw data to compose a "cooked"  # object with useful data  [PSCustomObject]@{ # when this was logged  Time = $_.TimeCreated # script code that was logged  Code = $_.Properties[2].Value # if code was split into multiple log entries,  # determine current and total part  PartCurrent = $_.Properties[0].Value PartTotal = $_.Properties[1].Value # if total part is 1, code is not fragmented  IsMultiPart = $_.Properties[1].Value -ne 1 # path of script file (this is empty for interactive  # commands)  Path = $_.Properties[4].Value # log level  # by default, only level "Warning" will be logged:  Level = $_.LevelDisplayName # user who executed the code (SID)  User = $_.UserId } } }  

Note that even when verbose script block logging is turned off, PowerShell will log certain code that is considered security relevant.

Twitter This Tip! ReTweet this Tip!