Problem description

A friend of mine asked why his PowerShell scripts (PowerShell profile) doesn’t execute properly after upgrading to PowerShell 5.0. A brief investigation showed that interactive PowerShell console runs in Constrained Language mode, as the result many language features are stripped out and PowerShell profile isn’t loaded with the following error:

Windows PowerShell
Copyright (C) 2015 Microsoft Corporation. All rights reserved.
 
C:\Users\vpodans\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 : Cannot dot-source this command because
it was defined in a different language mode. To invoke this command without importing its contents, omit the '.'
operator.
At line:1 char:1
+ . 'C:\Users\vpodans\Documents\WindowsPowerShell\Microsoft.PowerShell_ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Microsoft.PowerShell_profile.ps1], NotSupportedException
    + FullyQualifiedErrorId : DotSourceNotSupported,Microsoft.PowerShell_profile.ps1


PS C:\Users\vpodans> [math]::Sqrt(1)
Cannot invoke method. Method invocation is supported only on core types in this language mode.
At line:1 char:1
+ [math]::Sqrt(1)
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : MethodInvocationNotSupportedInConstrainedLanguage

PS C:\Users\vpodans> $ExecutionContext.SessionState.LanguageMode
ConstrainedLanguage
PS C:\Users\vpodans>

My friend uses Software Restriction Policies (SRP) to protect the system from accidental unapproved software/script execution. When he disables SRP, the error goes away and interactive console works normally. In addition, he pointed to a relevant thread in internet forums: SRP Whitelist Causing Odd Behavior in PowerShell v5

Detailed issue investigation

We started more detailed investigation and discovered very interesting things. First, I asked to examine the SRP log and we quickly found two suspicious entries:

powershell.exe (PID = 5140) identified C:\Users\shs\AppData\Local\Temp\halnmb3v.hmg.ps1 as Disallowed using default rule, Guid = {11015445-d282-4f86-96a2-9e485f593302}
powershell.exe (PID = 5140) identified C:\Users\shs\AppData\Local\Temp\xhltufae.z1d.psm1 as Disallowed using default rule, Guid = {11015445-d282-4f86-96a2-9e485f593302}

We identified that when PowerShell starts, it attempts to execute two scripts (.ps1 and .psm1 together) with random names from user’s temp folder. I quickly realized that PowerShell uses these files to determine whether Application Whitelisting (AWL) software is running or not.

I will use AWL or Application Whitelisting software to refer to both, SRP (Software Restriction Policies) and Applocker features. 3rd party AWL tools may apply too.

Quick google search returned a blog post from PowerShell Team: PowerShell ♥ the Blue Team. Among other things, I found a relevant section: Constrained PowerShell:

When a system is sensitive, one of the most powerful ways to limit the damage an attack can have is to reduce the capabilities of that attack. Windows’ security controls come in many forms – creating a hierarchy of protections that incrementally add value.

 

These protections are, of course, in addition to the regular Windows user permissions model. Applications don’t need to prevent users from modifying system-wide registry keys because Windows itself enforces those protections.

 

The strongest form of protection is when a system employs AppLocker in ‘Allow Mode’, where only specific known applications are allowed to run.

Prior to PowerShell version 5, a limitation of AppLocker’s ‘Allow Mode’ was that interactive PowerShell input was not subject to this policy. While Allow Mode might prevent unknown PowerShell scripts from running, it would not prevent the equivalent commands entered at an interactive prompt.

 

In version 5, PowerShell now reduces its functionality to “Constrained Mode” for both interactive input and user-authored scripts when it detects that PowerShell scripts have an ‘Allow Mode’ policy applied to them. Constrained PowerShell limits the language mode to Constrained Language (as described in about_Language_Modes), a mode first introduced for Windows RT.

 

Constrained Language doesn’t limit the capability of the core PowerShell language – familiar techniques such as variables, loops, and functions are all supported. It does, however, limit the extended language features that can lead to unverifiable code execution such as direct .NET scripting, invocation of Win32 APIs via the Add-Type cmdlet, and interaction with COM objects.

And here we go! In short, starting with PowerShell 5.0 a new so-called “security feature” is added:

  • if you are using Application Whitelisting software, PowerShell will reduce its functionality in interactive sessions by running them in constrained language mode
  • If you are NOT using AWL software, NO restrictions will apply to your interactive session and the session will run in Full Language mode

This raised a lot of questions.

PowerShell Team decision

At first sight, this looks very weird: If you do not use AWL to protect the system from accidental code execution, PowerShell won’t do that either. Run whatever you want. If you are using a sort of protection, PowerShell will enforce its own protection mechanisms. I contacted PowerShell team to get some more details about this design and got some response from Lee Holmes:

Here’s the design when the system has been configured with an ‘Allow List’ (scripts by default blocked):

  • Interactive use is moved to ConstrainedLanguage, otherwise PowerShell’s .NET capability can be used to bypass any ‘Allow List’ you might create, or can be used to create any kind of malware an author might desire. This is the delivery vehicle of most malware right now: “PowerShell –Command <Evil Here>”
  • Scripts that are disallowed by the policy also run in ConstrainedLanguage.
  • Scripts allowed by the policy run in FullLanguage.

Further discussion led us to nowhere, so I’m not going to quote the rest conversation. However, the idea is pretty clear: since PowerShell become popular, attackers are tend to use PowerShell code to exploit the system. This makes sense since it is a bit easier to write harmful code in PowerShell rather than in other languages. Though, C# is not much complex as well. But in any way, the problem exists, PowerShell provides too big opportunities to attacker.

Why I consider this design bad?

I would agree with PowerShell Team’s decision if it wouldn’t be selective and biased. I’ll attempt to describe my vision of the problem. I would like to hear alternative opinions as well.

What is security and what is not

Microsoft considers this feature as a “security feature”. In short: if an attacker is able to bypass AWL layer, constrained language mode will stop them. Let’s look into this in more details.

Microsoft implementation of AWL – Software Restriction Policies and its successor Applocker are primarily designed to help organizations to control the software used on enterprise computers. By the time, many (ok, very few, but in any way) systems administrators found that it is a good security layer that prevents users from accidental malicious code execution. There are a lot of invasion cases when users blindly clicks on unknown links in IM, mail clients, web sites, open unknown attachments and so on. When you do that, a code is dropped to user’s temp folder and it starts to work. Antivirus software isn’t much helpful due to its low detection ability, especially on new treats. By enabling AWL, the chances of accidental code execution become pretty low, because it literally blocks everything what is not explicitly allowed by systems administrators. This nice feature added a popularity to AWL.

Although AWL is effective from *accidental* unapproved code execution it isn’t a 100% protection either. It is important to not overrate the tool and clearly understand what each tool can what can’t. Both, SRP and Applocker have known flaws and they won’t help from targeted attacks. This means that you want it hard, you can bypass SRP/Applocker restrictions even with standard user privileges. I have a lot of material on this in appropriate blog categories: SRP and Applocker. For example SRP/Applocker doesn’t protect from malicious app macros (in Microsoft Office, Adobe Reader and so on). Prior to Windows 8 it was possible to run the arbitrary code by explicitly disabling SRP/Applocker via CreateRestrictedToken function call with appropriate parameters. This makes it clear that neither, SRP nor Applocker are security boundary. Recent publications about AWL exploiting by using regsvr32 prove that. Someone opened a case against MSRC (Microsoft Security Response Center): Microsoft-Applocker-Bypass. The response by MSRC was pretty obvious:

msrcresp_orig

 

Hi Kasif,

 

Thank you for contacting the Microsoft Security Response Center (MSRC).

 

Applocker is not a security feature. This feature was never designed as a security boundary. Instead, it was designed for compliance – to help organizations control which application are in use in their environments, reduce helpdesk calls regarding unauthorized software, etc. As such, MSRC does not consider methods that bypass this feature to be security vulnerabilities.

MSRC even doesn’t consider AWL as a security feature. Due to unknown reasons PowerShell Team considers AWL as a security feature and made their security feature dependent on non-security feature. WTF??? Is this a real security?

Broken usability

Among other features, interactive PowerShell console was a big WIN comparing to VBS in 2006. After 10 years, interactive console become very beloved by systems administrators and a lot of various administrative tasks are performed from the interactive console. We use interactive console to write and test scripts and investigate issues when they happen.

However, Application Whitelisting users are no longer able to do that. They even can’t debug their scripts in PowerShell ISE, because ISE debugger runs in constrained language mode too. You can’t do much in constrained mode. What does it mean? It means that AWL users will have to DISABLE their AWL software in order to write/debug their scripts and use interactive console. And you call this a “security”?

Broken dot-sourcing

If we look at the first error message, we get pretty clear statement: no more dot-sources. Personally I have pretty rich PowerShell profile which adds very handy functions and aliases to make experience in interactive console more productive. Even if your file with functions is properly signed and allowed by AWL, dot-sourcing won't work. PowerShell team explained it as follows:

One danger with trusted scripts (that can do anything) is that all functions and code within them also run as trusted. We disabled dot-sourcing of these scripts so that users can’t try to exploit internal functions. For example, a script that contains an “InvokeExpressionHelper” function could be dot sourced, and then the attacker could run: “InvokeExpressionHelper -Script ‘[SomeEvil]::DotNet’”.

 

Profiles are dot-sourced, so they run into this error if they are trusted / signed. You can still have a profile run and configure the session, just don’t mark it as trusted / signed. If your profile needs to do trusted stuff, you can have it call a separate script that is trusted / signed.

We no longer can dot-source/import functions from signed profile. Really?

Import-Module $profile

and profile with its functions is literally dot-sourced in full language mode. Technically speaking, module import and dot-sourcing are nearly the same thing, just called differently. With new "security" one method doesn't work and another does. Where is the security in this case?

Import-Module cmdlet allows to dot-source any script. It doesn't require a .psm1 or .psd1 file extension. File with .ps1 extension will be imported as well.

Where is the real problem?

Ok, we identified that AWL is not a security boundary and even a security feature (though, it provides some basic security coverage). This means that an attacker is certainly able to bypass AWL. PowerShell Team decided to make this “security” feature dependent on AWL. Since AWL is bypassed by an attacker already, they are free to elect a tool to exploit the system. PowerShell could be a simple decision.

However, PowerShell is not the only tool to run malware code. For example, C# code isn’t much harder than PS and default .NET installation on my test Windows 8.1 box contains a csc.exe tool, which is a C# compiler. Drop sources, compile them and execute. There are hundreds ways to execute arbitrary code after bypassing AWL. PowerShell MVP Joel Benett proposed that the hole in PowerShell allows to run in-memory code (without having to drop physical files on the disk). I haven’t tested it yet, but some search suggests that it is possible with C# compiler.

This makes all users (who use AWL and who doesn’t) equally vulnerable to PowerShell.exe –command security hole. As it was noted, new security feature is very dependent and does not apply to non-AWL users. Once again, where is the security when most users are still vulnerable?

Closing and opening security holes

Long time ago I played with PowerShell V2 CTP3 (yeah, it was really long ago) and found that PowerShell is covered by SRP even though PS1 files are not listed in SRP: Первые впечатления от PowerShell V2 CTP3 (sorry, the article is available in Russian only). And we (AWL users) appreciated that, because we got another way to control PS script execution along with PS execution policy. We just organized script locations and wrote appropriate rules in AWL.

However, in PowerShell 5.0 the things were changed back: PS1, PSM1, PSD1, PS1XML files are no longer covered by AWL. This means that you no longer able to restrict PS script execution! All scripts will be executed. Some in full language mode, some in constrained language mode. And explicitly disallowed rules won't help you.

To be honest, previously you could bypass execution policies and AWL policies by using PowerShell.exe –command. So, actually nothing significantly is changed. However, it is now a bit misleading, because AWL reports that the script is blocked, but in fact it isn’t true.

Conclusion

From my understanding PowerShell Team didn’t improve security by running PS console in constrained language mode. This is because most users are not protected. An attempt to make this feature dependent on AWL status is failure from the start. However, the job is done and interactive console experience, script writing and debugging for AWL users is broken permanently without any security improvement. Moreover, this will lead AWL users (systems administrators) to remove AWL in order to get PS stuff working. And this will make things even worse.

I can speculate that PowerShell Team enforced constrained mode only to AWL users because of their low count. I would imagine if they would enforce constrained mode to all users. They would be full of support tickets from entire world. But it is another story.


Share this article:

Comments:

Adam Russell

The article that you have quoted from 'Constrained PowerShell: https://blogs.msdn.microsoft.com/powershell/2015/06/09/powershell-the-blue-team', goes on to say: "Scripts that are allowed by the AppLocker policy (for example: signed by the enterprise’s trusted code signing certificate, or in a trusted directory) are not subject to Constrained Language."

The problem is that no such provision has been made for Software Restriction Policies (AppLocker’s predecessor). Therefore creating an SRP path rule to a script with the security level set to ‘Unrestricted’ will NOT prevent the script from running is Constrained Language Mode.

I can't understand why the PowerShell team chose to enforce Constrained Mode on systems that are subject to SRP (where the security level is set to ‘Disallowed’ and the enforcement is set to apply to ‘All users except local administrators’) without providing a mechanism for bypassing it for trusted scripts.

I logged this as a bug on UserVoice in April, but have yet to receive any feedback from Microsoft: https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/13332018-software-restriction-policy-constrained-language

buherator

"Since AWL is bypassed by an attacker already, they are free to elect a tool to exploit the system.  PowerShell could be a simple decision. However, PowerShell is not the only tool to run malware code."

There seems to be a misunderstanding here: Attackers don't use PS because it is easier to write than C# (or any other language) but because "Prior to PowerShell version 5 [...] interactive PowerShell input was not subject to [Applocker Allow Mode] policy". So even if AWL was in effect, you could use interactive PS to bypass it. In other ways, prior AWL bypass was not required, PS was the bypass. And this is was Constrained mode is about. 

Vadims Podāns

> So even if AWL was in effect, you could use interactive PS to bypass it. In other ways, prior AWL bypass was not required, PS was the bypass.

I got your point, but this still doesn't make a sense. Constrained mode is not designed to protect from AWL bypass and it doesn't. I explained my opinion in next blog post: https://www.sysadmins.lv/blog-en/powershell-50-and-applocker-when-security-doesnt-mean-security-part-2.aspx 

RaymondTH

Hi from 2018, Its not a fix but this is as secure as we could make it

Adding a path rule for 

%OSDRIVE%\USERS\*\APPDATA\LOCAL\TEMP\__PSSCRIPTPOLICYTEST_*

Allows full mode, Once we did this our powershell wrapped installers would run. 

Grigory

RaymondTH,

Adding an allow rule to a user-writable path defeats the purpose of AWL, no-no.

Kim Oppalfens

The main issue seems to be interactive shells for the people that develop scripts. So why don't you give those a path rule that allows them to run in full language mode? If they are supposed to be able to run anything in interactive mode anyway, there's no protecting them.

You can argue ad nauseum that this isn't a security feature because it can be bypassed. But by that standard, nothing is a security feature or boundary, as just about anything has flaws that allows bypasses. The simple reality is that this stops a ton of automated attacks by attackers that didn't go the extra mile to include an AWL bypass. Your policy building skills can stop some of those bypasses as well.

Enable AWL & Constrained language mode for everyone in your company that never ever runs any code that wouldn't work in constrained language mode. Enable AWL & create a rule to allow full language mode for everyone that does developcode.

As to not adding an allow rule to a user-writable path being a no-no, sure. But if you require an interactive shell that allows anything to run during code devolepment, allowing that or a file in a user-writable path are equally insecure.

David

This isn't a technical problem.  The security folks at Microsoft adapt an "attacker" mindset but have never spent a single day as sysadmins in an average company - responding to requests form management, pressured by project managers and looming deadlines.

I'm managing a blue team in a large corporation and can't count the number of times Constrained Mode, Device Guard, Applocker and the other nonsense came back to bite us in the butt.  A typical scenario: a sysadmin spends a few hours trying to get a simple task like loading an approved module or debugging a script, for crying out loud! and the security features stop them.  Eventually the sysadmin - whose job is to -- well, administer systems -- gives up and schedules the script to run daily as Administrator.  Thanks to Microsoft Security we are all safer.

Kim Oppalfens

David,

How is any of that not solved by giving the sysadmin a system he can debug on and signing the code he develops? Just because a security feature can't be deployed to 100% of your devices shouldn't mean it's dismissed for 100% of the devices.

Kim


Post your comment:

Please, solve this little equation and enter result below. Captcha