PowerShell: the Killer Queen

Last Updated on September 28, 2022 by rudyooms

This blog will show you which options you have in Intune when you want to deploy a PowerShell script with an HKCU registry change but of course, you blocked PowerShell.exe on your Windows Endpoints. I am also going to explain why you need to block PowerShell or which defenses you need to put in place when you are allowing it.

I will divide this blog into multiple parts

  1. Introduction
  2. Securing PowerShell
  3. Blocking PowerShell
  4. Solving the Applocker Blockade

1. Introduction

When you are allowing your employees to run PowerShell you could be exposed to an Insider threat. The employee who has access to PowerShell could misuse his access. PowerShell can be used for initial access, local reconnaissance, privilege escalation, and lateral movement.

Command and Scripting Interpreter: PowerShell, Sub-technique T1059.001 – Enterprise | MITRE ATT&CK®

PowerShell access could be the first phase (reconnaissance) into “hacking”.  There are a lot of techniques available which can be used with PowerShell. The damage that could be done depends on what kind of setup you have. When all of your devices are Azure Ad Joined and there are no on-premise servers left, the impact is significantly lower than with a hybrid setup. With a hybrid setup, there could be a lot of on-premise servers left that could be targeted.

We also must not forget PowerShell Scripts are a key ingredient in many malware attacks. So what options do we have? Are we going to allow it with no security in place or are we going to secure and protect PowerShell

2. Securing PowerShell

Let’s talk about hardening PowerShell first.  With the release of PowerShell 5.0, there are a lot of security features added. When your Windows 10 devices are up to date, you have these security features at your disposal. Beware! Most of them need to be enabled or configured manually.

1. Antimalware Integration (AMSI)

The Windows Antimalware Scan Interface (AMSI) is a versatile interface standard that allows your applications and services to integrate with any antimalware product that’s present on a machine. AMSI provides enhanced malware protection for your end-users and their data, applications, and workloads. The AMSI feature is integrated into PowerShell.

2. Script Block Logging

When you enable Script Block Logging, PowerShell records the content of all script blocks that it processes. Once enabled, any new PowerShell session logs this information.

# Module Logging
$RegistryPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ModuleLogging"
$Name = "EnableModuleLogging"
$Value = "1"
If (!(Test-Path $RegistryPath)) { # Value Doesn't Exist, so create it
New-Item -Path $RegistryPath -Force | Out-Null
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
Else {
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
$Name = "ModuleNames"
$Value = "*"
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType String -Force | Out-Null
# Script Block Logging
$RegistryPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
$Name = "EnableScriptBlockLogging"
$Value = 1
If (!(Test-Path $RegistryPath)) { # Value Doesn't Exist, so create it
New-Item -Path $RegistryPath -Force | Out-Null
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
Else {
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}

3. System Wide Transcripts

If you enable this function, the system-wide transcription feature logs PowerShell use on a per-user basis. If you enter a PowerShell command, a record of that command is written to a document within the output directory

$RegistryPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\Transcription"
$Name = "EnableTranscripting"
$Value = "1"
If (!(Test-Path $RegistryPath)) { # Value Doesn't Exist, so create it
New-Item -Path $RegistryPath -Force | Out-Null
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
Else {
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
$Name = "EnableInvocationHeader"
$Value = "1"
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null
$Name = "OutputDirectory"
$Value = "C:\Temp\PSLogs"
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType String -Force | Out-Null 

4. Constrained PowerShell

Constrained Language consists of a number of restrictions that limit unconstrained code execution on a locked-down system.

Configure Applocker to enforce constrained Powershell language mode. The only thing you will need to configure in Applocker is the Script Rules. Scripts that are allowed in the trusted directories or signed by a trusted code-signing certificate will run in full language mode all other scripts will be run in constrained language mode.

Another possibility would be configuring Device Guard (UMCI). But if you are new to securing devices, stick to Applocker for now. In my opinion managing, Applocker on an enterprise-scale is a lot easier, and configuring an exclusion can be done a lot faster.

Please note: I will assume we all made sure, all users are not local admins!

Adminless – Call4Cloud

5. Powershell 2.0

So you are all good? Nope. PowerShell 2.0 is still installed by default in Windows 10.

Beware, when you installed dot.net  3.5 because some apps are requiring it, you could launch a PowerShell -version 2

So please make sure you remove it:

Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root

3. Blocking Powershell

Now we have a pretty good understanding of how to secure PowerShell, I will show how you need to block PowerShell and how you could bypass the issue when you want to deploy user based PowerShell script in Intune.

Some time ago I was speaking with Damien van Robaeys about the systray tool he created.  It’s a wonderful tool if PowerShell is allowed to run. For most of our customers, PowerShell is blocked so running this tool was no go. I decided to start converting the app to EXE files. While I was busy converting it, I realized this also could be the solution when you want to deploy user-based PowerShell scripts but PowerShell is blocked.

First some background information about blocking PowerShell. When you have deployed AppLocker with the default rules, PowerShell is not blocked! Please take a look at my AppLocker baseline, PowerShell will be blocked when deploying this baseline.

Applocker à la minute – Call4Cloud Powershell Automated

But not every company has blocked PowerShell! Why you might ask? There could be a lot of really good reasons why most of the companies are not blocking PowerShell. One of them is definitely the need to configure some HKCU registry settings, by deploying an Intune PowerShell script. You can guess what happens when you blocked PowerShell.

First, let’s see what happens when you want to deploy an HKCU registry setting and PowerShell is blocked. Before we are publishing the script, we need to know the difference between running the script in the User or System context.

User Context –> When you want to deploy user settings –> Bound to the security applied to the user

System Context –> When you want to deploy system settings –> Not bound to the security applied to the user

You can take this sample script and upload it to Intune and select: “Run this script using the logged-on credentials” and set it to yes.

$registryPath = "HKCU:\Software\call4cloud\Scripts"
$Name = "Version"
$value = "1"
IF(!(Test-Path $registryPath))
  {
    New-Item -Path $registryPath -Force | Out-Null
    New-ItemProperty -Path $registryPath -Name $name -Value $value `
    -PropertyType DWORD -Force | Out-Null}
 ELSE {
    New-ItemProperty -Path $registryPath -Name $name -Value $value `
    -PropertyType DWORD -Force | Out-Null}

When you deployed the script, watch the Applocker event log. You will notice within a few minutes PowerShell was prevented from running and a nice Applocker Event 8004

4. Solving the Applocker Blockade

But what If there are different options available on how you could deploy user settings but without using PowerShell Scripts? A year ago I wrote a blog about how you could configure some HKCU settings without the need for PowerShell to be allowed in the user session.

How to deploy HKCU registry changes while blocking powershell. – Call4Cloud

Of course, this option works pretty well but after spending some time trying to convert the PowerShell tool from Damien to an EXE I realized the same can be done when you want to deploy user settings.

You could also use this ps2exe tool instead to deploy PowerShell scripts in Intune. So why not convert your PowerShell scripts to executable files?  To do so, you will need to install this module first.

With a simple command line, you can convert the PS1 to an executable file ….

Please Note: Ps2exe and defender don’t go well together, so you could also switch to ieexpress as I am mentioning in this blog:

Getting back your encrypted Win32Apps from Intune (call4cloud.nl)

After we converted it to an executable we can create an Intunewin file and upload it to Intune. It might be useful, to create an additional file in your PowerShell script so you can set this file in the win32 app detection rule.

After you have made sure the app is installed on the device open the AppLocker event log, and you will notice the file is allowed to run from the %windir%\imecache folder.

The Microsoft Intune Management extension downloads the file into the  C:\Program Files (x86)\Microsoft Intune Management Extension\Content and unzips it to the %windir%\imecache folder. That’s great because this folder is NOT blocked by default or in my AppLocker baseline or in the default Applocker rules.

To be sure, open the registry to check if the registry key is created. As shown below, it’s created without any issues.

Conclusion:

Allowing PowerShell to make your life easier to deploy user settings is not necessary anymore.  You have the option to block it and work around the blockade.

Now I showed you the options available, go and block PowerShell…………or harden PowerShell but please do not allow it without any security barriers or logging.

Please note: Before PowerShell 5.0, PowerShell was the ultimate hacking tool but now with some additional security features it made PowerShell a little bit more secure.

If you want to read some more information about red teaming, attack simulation, and other IT security stories from the trenches without using PowerShell.

Publications | Outflank

One thought on “PowerShell: the Killer Queen

  1. Hi Rudy,
    Thanks for writing another great article. Much appreciated.

    Now, the following question arises in my mind as I am struggling with this: If for example a Win32App has been created, which installs under the user context, the detection script also runs from the user context. As AppLocker is configured to prevent users from running “%SYSTEM32%\WINDOWSPOWERSHELL\V1.0\POWERSHELL.EXE”. How would you approach running a custom detection script under the user context as detection cannot take place because PS is prevented from running?

    Thanks in advance!

Leave a Reply

Your email address will not be published. Required fields are marked *

4  +  2  =