【技术分享】CVE-2017-8715:PowerShell模块清单文件绕过安全补丁

http://p6.qhimg.com/t010c837639c29f3491.png

译者:shan66

预估稿费:180RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

前言

最近,我和Matt Graeber(@mattifestation)一直在钻研绕过Device Guard上下文中用户模式代码完整性(UMCI)检测的方法。在这个过程中,我们发布了一系列的CVE,与此同时,微软也通过不断改进约束语言模式(CLM)——Period Guard和AppLocker的主要PowerShell策略执行机制,缓解PowerShell对UMCI的攻击。


CVE-2017-0218

我们知道,绝大多数注入漏洞都会滥用带有微软签名的PowerShell脚本或包含运行未签名代码功能的模块。这个范围实际上是很广的,从调用参数的Invoke-Expression一直到PowerShell的Add-Type cmdlet的自定义实现,皆在该范围之内。微软签名的PowerShell代码是有针对性的,因为赋予微软代码执行权限的AppLocker或Device Guard策略将会以全语言模式来执行——也就是说,在可以执行什么代码方面,没有加以限制。

为了充分了解微软所进行的修补工作,我们不妨先来考察一下程序在修补之前的行为。这个漏洞的主要问题是,攻击者可以使用带有微软签名的PowerShell脚本中的函数来绕过UMCI。为了把这个问题搞清楚,我们来看一下脚本“utils_SetupEnv.ps1”,该脚本是位于C:Windowsdiagnostics下面的Windows Troubleshooting Packs的一个组件。这个脚本含有一个名为“import-cs”的函数。这个函数的作用就是接收C#代码,然后为其调用Add-Type(注意:Add-Type在约束语言模式下是禁止使用的)。 由于该脚本带有微软的签名,因此,它会以全语言模式运行,也就是说,该漏洞允许攻击者执行任意C#代码。

http://p8.qhimg.com/t015be5eb3019030727.png

图1

正如你在上面看到的那样,这里导入了“utils_SetupEnv.ps1”,它为我们提供了“import-cs”函数。 在使用这个函数的时候,我们可以传递自己的C#代码,从而绕过约束语言模式。上面的漏洞已经在CVE-2017-0218中说明了,现在该漏洞已经得到了修复。


缓解措施

为了应对类似上面这种漏洞,微软为运行在PowerShell约束语言模式下的代码强加了一些额外的限制。第一个限制就是不允许通过Import-Module或其他方式导入PowerShell脚本(.PS1)。 如果您尝试在CLM中导入脚本的话,将会看到如下所示的内容:

http://p1.qhimg.com/t01554cec86033723a8.png

图2

对于这个限制措施,一种可能的绕过方法是将PowerShell脚本(.PS1)重命名为模块文件(.PSM1)并以此方式导入。为了应对这种绕过方式,微软又引入了第二个缓解措施。

微软对通过PowerShell模块(.PSM1)导入和使用的内容做出了相应的限制。这一点是通过“Export-ModuleMember”完成的。其中,Export-ModuleMember的作用就是定义了在导入模块后,允许使用模块的哪些函数。在约束语言模式下,模块的函数必须通过Export-ModuleMember导出后才能使用。 这大大减小了滥用带有微软签名的PowerShell模块中的函数的攻击面。 一般来说,显式定义要向模块的用户公开哪些函数也是一个非常好的方法。

如果我们将“utils_SetupEnv.ps1”重命名为“utils_SetupEnv.psm1”,并尝试导入的话,看上去会非常顺利。但您可能会注意到,之前使用的“import-cs”函数没有被识别出来。这是因为“import-cs”函数不是通过Export-ModuleMember向外公开的。

http://p1.qhimg.com/t01a942dc558af5d4a1.png

3

当我们查看一个正常的PowerShell模块文件的时候,会发现Export-ModuleMember的定义将如下所示:

http://p2.qhimg.com/t0119d300ff617d85c4.png

图4

这实质上意味着,在导入模块时只能使用“Export-ODataEndpointProxy”。这极大地限制了带有微软签名的PowerShell脚本被滥用的可能性,尤其是那些包含可能被用来运行未签名代码的函数的脚本,因为大部分函数都不会公开。同时,PowerShell团队也正在稳建地处理被用来规避约束语言模式的各种原语。


CVE-2017-8715

在研究了这个补丁之后,我发现并报告了一个与Export-ModuleMember有关的漏洞,该漏洞的编号为CVE-2017-8715,并在十月份发布了相应的安全补丁。这个安全措施绕过漏洞是通过滥用PowerShell模块清单(.PSD1)来实现的。在研究这些文件的影响时,我意识到可以通过这些文件来配置模块行为,而且它们不像其他PowerShell文件那样具有签名方面的限制(可能因为PSD1文件不包含可执行代码的缘故)。尽管没有这些限制,人们仍然可以对PSD1进行签名。

我们再次回到“utils_SetupEnv.ps1”脚本,发现“import-cs”函数无法使用,因为该脚本中的任何函数都无法通过Export-ModuleMember公开。为了解决这个问题,我们可以将“utils_SetupEnv.ps1”重命名为“utils_SetupEnv.psm1”,这样我们就可以导入它了。进行重命名之后,我们就可以删除为我们导出“import-cs”函数的“utils_SetupEnv.psm1”模块所对应的模块清单了。这个模块清单的内容如下所示:

http://p8.qhimg.com/t01481dce55e6108037.png

图5

如您所见,我们已经将“import-cs”设置为一个通过“FunctionsToExport”导出的函数。这样的话,就能像Export-ModuleMember那样导出函数了。由于PowerShell的模块清单文件不像其他PowerShell文件那样具有签名要求,因此我们可以直接为我们想要滥用的带有Microsoft签名的脚本创建自己的模块清单文件。在删除了PowerShell模块“utils_SetupEnv”的上述清单后,就可以绕过微软新引入的.PS1与Export-ModuleMember相关的安全措施,并能使用“import-cs”函数执行任意C#代码了。

http://p8.qhimg.com/t01cc6baaeed8a1827f.png

图6


总结

如前所述,这个绕过漏洞已经通过CVE-2017-8715公之于众了。相应的补丁要求PowerShell模块清单文件(.PSD1s)与模块中的所有其他文件那样,必须提供相应的代码签名,也就是说,即使某个模块带有相应的签名,并满足Device Guard或AppLocker策略的要求,其模块清单也必须具有与白名单规则一致的签名才行。这样做可以防止攻击者修改现有的清单或使用他们自己的清单文件进行攻击。

(完)