译者:WisFree
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
概述
这篇文章将给大家介绍一种微软Device Guard(设备保护)用户模式代码完整性(UMCI)绕过技术,由于csc.exe在对C#代码进行动态编译的过程中并不会对代码完整性进行检测,而我们的这项技术利用的正是这一点。安全研究人员早在2016年11月14日就已经将这个问题上报给了微软公司,但微软直到目前都还没有决定要去修复这个问题。我们可以通过禁用csc.exe来缓解这项绕过技术,但考虑到很多合法代码和PowerShell模块都需要使用类似msbuild.exe和Add-Type(允许用户在Windows PowerShell会话中定义一个Microsoft .NET Framework类)这样的模块,所以这个缓解方案也许并不适用于你的环境。
介绍
当Windows系统启用Device Guard UMCI(用户模式代码完整性)来验证程序签名和权限时,除了会屏蔽应用白名单中没有许可的代码之外,系统还会根据预先制定的策略仅允许已签名的脚本运行(例如PowerShell和WSH)。UMCI在PowerShell中采用的是一种约束语言模式的执行策略,而限制语言模式的一项重要功能就是防止未签名或未许可的脚本调用Add-Type,否则这些脚本将能够通过编译并加载C#代码来实现任意代码执行。不过需要注意的是,Device Guard代码完整性(CI)策略准许运行的脚本不仅不会受到这种限制的,而且还能够以完整语言模式运行并允许调用Add-Type。
在研究Device Guard的绕过技术时,我们假定目标是合法代码,并允许调用Add-Type。根据我们目前已经知道的信息,调用Add-Type的行为将导致csc.exe(C#编译器)在%TEMP%目录下生成一个.cs文件,随后编译器便会编译这个文件,并加载它。PowerShell调用Add-Type的常规过程如下所示:
看到上图所示的这些文件被创建出来之后,我问了自己以下几个问题:
1.如果一个PowerShell函数跟很多微软签名的模块函数一样能够被允许(例如白名单策略)去调用Add-Type的话,我可以在源代码的编译和加载过程中,及时用自己的恶意.cs文件迅速替换掉csc.exe生成的.cs文件吗?
2.上图所示的过程中生成了一个.DLL文件,我可以对这个DLL文件下手吗?它是否也得遵循代码完整性检测呢?
研究方法论
我们先考虑一下上面的第二个问题,因为如果代码完整性策略(CI)能够阻止攻击者加载未经签名的恶意DLL文件,那么我们就不可能利用这个漏洞了。那么为了回答刚才的第二个问题,我需要确定Add-Type被调用时哪一个.NET方法被调用了。这里我们可以使用dnSpy来追踪方法的调用情况,在dnSpy的帮助下,我检测到了以下.NET方法的执行:
Microsoft.PowerShell.Commands.AddTypeCommand.CompileAssemblyFromSource
System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource
Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch
Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch
Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch
Microsoft.CSharp.CSharpCodeGenerator.Compile (where csc.exe is ultimately called)
System.Reflection.Assembly.Load
当Microsoft.CSharp.CSharpCodeGenerator.Compile方法被调用时,也就是csc.exe最终被调用的时候。当编译操作完成之后,FromFileBatch会将编译结果以字节数组的形式读入,然后使用System.Reflection.Assembly.Load(byte[], byte[], Evidence)方法来加载该数组。这也就意味着,如果能够劫持原本合法的.cs文件,我就可以执行任意未签名的代码了。
漏洞利用
我自己编写了一个名叫Add-TypeRaceCondition的函数,这个函数可以接收攻击者提供的C#代码,系统将会允许它调用Add-Type,并在约束语言模式下对其进行编译和加载。这项绕过技术的实现思路大致如下:
1.生成一个PowerShell子进程,并不停地尝试向%TEMP%目录中注入恶意.cs文件。
2.将PowerShell子进程的优先级调到最高,以此来提升我们跑赢Add-Type调用流程的可能性。
3.在PowerShell父进程中,导入一个能够调用Add-Type且经过微软签名的PowerShell模块,我在这里选择使用的是PSDiagnostics模块。
4.终止PowerShell子进程。
5.现在,我们所导入的恶意代码可能已经替换掉了PSDiagnostics中原本的合法代码,并成功加载完毕。
在真实场景下,这项绕过技术的成功率可以超过五成。如果你第一次尝试时Add-TypeRaceCondition没有正常工作的话,你再试一次应该就会成功了。
请大家注意,我在实现这项绕过技术时使用了PowerShell,但理论上来说我们可以使用任何能够迅速重写目标.cs文件的工具来实现绕过,例如批处理脚本或VBScript等等。如果感兴趣的话,读者也可以选择自己喜欢的编程语言来尝试实现本文所介绍的这项绕过技术。
操作注意事项
需要注意的是,虽然本文介绍的是一种应用程序白名单绕过技术,但攻击者还可以用它来实现任意代码执行并绕过目标系统所采用的安全防御机制。在实现这种绕过技术的过程中,攻击者只需要向目标系统的磁盘中注入一个C#文件,该文件可以在目标磁盘中临时生成一个DLL文件,而这个文件会被立刻删除。不过具体情况还得取决于攻击者所使用的Payload,因为某些具有实时检测功能的反病毒解决方案和可能会在System.Reflection.Assembly.Load调用这个DLL文件之间就发现它并将其隔离了。
防御策略
首先我要强调的是,这是一个.NET漏洞,而并非PowerShell的问题,我们这里只是为了更方便地实现这种绕过技术所以才选择使用了PowerShell。正如我之前所说的,这个问题并不仅仅是在PowerShell调用Add-Type时才会出现,任何调用CodeDomProvider.CompileAssemblyFrom方法的应用程序都会存在这种绕过问题。可以预测到的是,攻击者还会继续利用那些能够调用这些方法的已签名应用来绕过Decice Guard,直到微软公司修复这个问题为止。
用户可以采用的一种缓解方案是利用Device Guard规则来屏蔽csc.exe,但我个人并不建议用户这样做,因为还会有很多合法的应用程序会通过PowerShell来调用Add-Type。我这里给大家提供了一个简单的Device Guard CI规则,如果用户愿意采用上述解决方案的话可以将其添加至自己的策略中。规则代码如下:
检测方法建议
不幸的是,由于程序在磁盘中生成的文件随后便会被立刻删除,而且System.Reflection.Assembly.Load(byte[])不会生成一个传统的模块加载事件(某些反病毒产品会以此作为检测标识),因此想要使用现成的免费工具来检测这种绕过行为几乎是不可能实现的。我个人认为,厂商可以考虑从csc.exe所生成的DLL文件入手,或者根据文件哈希来检测可疑行为。
需要注意的是,我这里故意没有提到用PowerShell v5 ScriptBlock来作为检测方法,因为在本文所介绍的绕过技术实现过程中,PowerShell并不是必须的。
总结
虽然我们可以通过本文所介绍的技术来绕过Device Guard的用户模式代码完整性策略,但我仍然对微软引入的这项安全保护技术保持乐观。这是一个非常难以解决的问题,因为Device Guard存在太多的攻击面了。在绝大多数情况下,我们可以通过引入代码完整性(CI)黑名单规则来缓解针对Device Guard UMCI的绕过技术。但不幸的是,在我看来真实的使用场景下几乎没有用户会专门引入针对这种绕过技术的缓解方案。除此之外,微软方面也并不认为这属于一种针对Device Guard的绕过技术,不过我也希望大家的信心和积极性不要因此而受到打击,我鼓励大家将自己能够发现的绕过漏洞尽可能地上报给secure@microsoft.com。我相信在我披露了这项绕过技术之后,微软公司最终还是会修复这个问题的。
可供参考的绕过技术