Constrained Language Mode and Powershell Core

Learn about how PowerShell Core on v6+ determines what language mode to use

Background

At work, we use Software Restriction Policies to prevent stupid malware or users from running stuff that really shouldn’t be run. Recently, a coworker was trying to use PowerShell Core for the first time and couldn’t figure out why he was unable to install modules or run things like [math]::Round(2.1). I had to do a little digging to figure out the exact cause and fix it for him.

History of Language Modes in PowerShell

PowerShell v3+ has had language modes to help reduce your organizational attack surface. Prior to PowerShell Core, PowerShell would (depending on version) create a file in your local appdata folder called AppLockermode.txt or create a random file via [system.IO.Path]::GetRandomFileName() (appending ps1 and psm1 over 2 files). The contents of that file would either be the text string 1 or # PowerShell test file to determine AppLocker lockdown mode (Markdown render issue: The string has a 60th character at the end that is a single space).

If the system AppLocker or other policies prevented the creation of the file(s), then PowerShell would know that this is a security minded system and set the session’s language mode to ConstrainedLanguage. If nothing failed, it would test the SaferIdentifyLevel (Thanks SeeminglyScience!) to accurately determine what language mode the session should run in. That check came presumably through TestSaferPolicy.

What PowerShell Core Does

In PowerShell Core (6+), the same approach is taken, except the contents of the file are dynamic based on the time. For me, that meant that I could no longer use a Software Restriction Policy hash rule to make PowerShell startup in FullLanguage mode. I don’t love adding path rules, but that’s the answer here. In PowerShell Core, a prefix was added that makes it easy to handle PowerShell Core startup: __PSScriptPolicyTest_.

Put that in the temp folder allowed execution list and you’ll be good to go. Of course, adding path rules like this means that you aren’t getting ANY security against threats that come from PowerShell. Make sure that doing something like this is supposed to be making PowerShell fully functional in your org!

Wrapping up

I think it is VERY cool being able to easily dig into the source code of PowerShell to figure out the cause of my language woes! This is obviously a VERY niche topic, but I know that it will help someone, somewhere.

Also, don’t use “%temp%” in your SRP rules. Doesn’t look like Windows 10 (at least) likes it.