在过去几个星期的时间里,我很荣幸能够与Matt Graeber(@mattifestation)和Casey Smith(@subtee)一起研究如何绕过Device Guard的用户模式代码完整性机制。Device Guard(设备保护)是Windows 10操作系统中新添加的安全保护功能,设备保护使用用户模式代码完整性(UMCI) 来确保在用户模式下运行的任何内容(例如某项服务、Universal Windows Platform (UWP) App 或经典 Windows 应用程序)都是受信任的,从而仅允许受信任的二进制文件在主机中运行。如果你不熟悉Device Guard的话,你可以参阅相关资料以获取更多关于设备保护功能的信息[参考资料]。
简而言之,Device Guard通过用户模式代码完整性机制来防止未经签名的代码在计算机中得到执行,限制Windows脚本主机的相关功能,并让PowerShell在受限制的语言模式下运行。
最近,@mattifestation在博客上发表了一篇关于如何在典型Device guard场景下使用已签名调试器WinDbg/CBD来执行shellcode的文章。在不久之后,@subtee也发表了一篇文章,并在文章中描述了如何在启用了Device Guard的系统中使用CSI.exe运行未签名的C#代码。
在他们两位专家的带领之下,我决定安装Visual Studio企业版(试用版),然后看一看里面到底有哪些二进制代码。在进行了大量的深入分析工作之后,我偶然间发现了dnx.exe,它是Microsoft .NET的执行环境。
技术分析
在Device Guard的场景中,dnx.exe是可以直接运行的。因为它是由微软签名的二进制代码包,而且Visual Studio企业版也自带了dnx.exe。为了在一台启用了Device Guard的系统中执行dnx.exe(假设目标系统中并没有安装dnx.exe),你需要下载dnx.exe并配置好相应的依赖环境,然后将所有的程序和代码发送至目标系统中(至于如何将其发送至目标系统中,就交给读者们自行解决啦!)。
完成了全部配置之后,我们现在就可以尝试绕过目标主机中Device Guard的UMCI了。由于dnx.exe允许在动态场景下执行代码,所以我们就可以用它来执行任意代码了,例如未经签名的C#代码。
比如说,我们可以创建一个名为“Program.cs”的C#文件,然后将我们想要执行的C#代码写入这个文件。为了更好地给大家演示如何执行未经签名的代码,我们在下方给出了一个简单的例子:
为了满足dnx.exe的运行条件,我们要提供一个Project.json文件,这个文件指明了代码在执行过程中所需的一些数据。在我们的演示过程中,我们所使用的“Project.json”文件来源于微软的官方博客[传送门]。正如微软在其博客中写到的那样,我们可以将“Program.cs”和“Project.json”放在一个叫“ConsoleApp”(文件夹名称可以自定义)的文件夹中,然后执行相应的C#代码。
执行结果
既然我们已经准备好了相应的文件,那么我们就可以使用dnx.exe来执行我们在“ConsoleApp”文件夹中存放的C#代码了。需要注意的是,这台设备已经启用了Device Guard。运行结果如下图所示:
正如你在上图中所看到的那样,我们未经签名的C#代码成功地在dnx.exe之中运行了。
缓解方案
幸运的是,我们可以通过代码完整性策略来缓解这个问题。我们可以在FilePublisher文件中定义相应的规则,关于规则的制定方法可以参阅这份资料[参考资料]。
这份Device Guard代码完整性策略由FilePublisher拒绝规则组成,仅供各位参考。除此之外,我们也会在GitHub上更新相关的缓解策略,希望各位保持关注。
你可以将下面这段代码写入你目前所使用的代码完整性策略中:
# The path to the denial policy from the GitHub repo $DenialPolicyFilePath = 'BypassDenyPolicy.xml' # Replace this with the file path of the policy you're using $ReferencePolicyFilePath = 'ReferencePolicy.xml' # Name this whatever you want $MergedPolicyFilePath = 'ReferencePolicyWithMitigations.xml' # Parse the rules from the denial policy $DenyRules = Get-CIPolicy -FilePath $DenialPolicyFilePath # Merge the rules into a new, merged code integrity policy Merge-CIPolicy -OutputFilePath $MergedPolicyFilePath -PolicyPaths $ReferencePolicyFilePath -Rules $DenyRules