CVE-2020-1170漏洞详解

  • A+

译文声明
本文是翻译文章,文章原作者itm4n,文章来源:https://itm4n.github.io
原文地址:https://itm4n.github.io/cve-2020-1170-windows-defender-eop/

前言

这是我的写的关于CVE-2020-1170的概述。这是Windows Defender中权限提升的错误,通过运行特制的应用程序,攻击者可利用该漏洞删除系统上任意文件,控制受影响系统。在安全产品中发现漏洞是非常令人兴奋的,虽然这也不是第一次了。我很惊讶在我之前没有人报告过此漏洞。

概述

在探讨漏洞细节之前,我先简单介绍一下整个时间线。我在8个月前就已经通过ZDI报告了此漏洞,但是他们表示对收购此漏洞并不感兴趣。当时,我只有几周的Windows安全研究经验。所以在收到他们的回复后,我暂时把对这个漏洞的研究工作放在了一边。在接下来的几个月,我甚至忘记了这个漏洞。

五个月后,也就是2020年的下半旬,我在笔记中看到了这份漏洞报告。此时我对漏洞研究已经有了一些经验。我深知这个漏洞的潜在价值,我决定继续对漏洞进行研究。这确实是一个相当正确的决定,我找到了一种更好的方法来触发漏洞。4月初,我向Microsoft报告了这个漏洞,几周后得到认可。

初探

在Microsoft发布的公告中,您可以看到:
Windows Defender中存在一个权限提升漏洞,该漏洞导致攻击者删除系统上任意文件。

问题出现在Windows Defender对日志文件的处理方式。Windows Defender使用了2个日志文件MpCmdRun.log和MpSigStub.log,它们都位于C: WindowsTemp中。该目录是系统公用的临时文件存储处也是SYSTEM账户的默认临时文件夹。默认情况下,管理员和SYSTEM对这些文件具有完全控制权,users组的用户甚至没有读取的权限。
::: hljs-center

1.png

:::

下图是MpCmdRun.log部分摘录,其中包含了病毒特征更新等事件,但是您还可以从中找到与防病毒扫描相关的条目。
::: hljs-center

2.png

:::

对于签名的更新是定期自动完成的,当然也可以使用Update-MpSignature PowerShell命令手动触发。该命令不需要任何特权,普通用户也可以触发更新。下图是运行示例:
::: hljs-center

3.png

:::

我们可以观察到,进程以NT AUTHORITYSYSTEM用户身份对C:WindowsTempMpCmdRun.logWindows Defender 进行写入。
::: hljs-center

4.png

:::

这意味着作为低权限的用户可以通过触发进程的方式以NT AUTHORITYSYSTEM身份向日志文件执行写入操作。尽管如此,我们仍然没有权限访问该文件,也无法控制其内容。我们甚至没有对Temp文件夹本身的写访问权,我无法把它作为漏洞利用点,我甚至想不到比这更鸡肋的攻击向量了。

但是,我并没有放弃,根据CVE-2020-0668的经验(在Windows Service Tracing中获取任意代码执行权限的漏洞),我意识到,这里不仅仅是日志写入这么简单。

我发现每次签名更新完成时(事实上,这种更新经常发生),都会向文件中添加一条新的条目,条目大小大概1KB。虽然看起来不多,但是日积月累呢?在这种情况下,通常会采取日志轮转机制的管理策略,以便对旧日志进行压缩,存档或删除。我想弄清楚MpCmdRun.log文件是否也采用了这种方式,如果是这样的话可能会有滥用特权文件操作的地方。

搜索日志轮转机制

为了找到潜在的日志轮转机制,我对MpCmdRun.exe进行了逆向分析。在IDA中打开文件后,第一件事就是搜索MpCmdRun字符串,这确实是一种高效的做法,
::: hljs-center

5.png

:::

我们第一个搜索到了字符串是MpCmdRun.log,这很常规。但是我们还发现了有趣的字符串,MpCmdRun_MaxLogSize。这正是我苦于寻找的日志回旋机制。我不假思索的服用了红色药丸,掉进了兔子洞中。(《黑客帝国》里最为经典的台词之一。这句话里的rabbit hole(兔子洞)是一种隐喻(metaphor),指的是“进入未知世界”,这层含义源自1865年的著名童话书《爱丽丝漫游奇境记》)

通过查看MpCmdRun_MaxLogSize的外部引用,发现它仅在MpCommonConfigGetValue()中被调用。
::: hljs-center

6.png

:::

而MpCommonConfigGetValue()函数又是被MpCommonConfigLookupDword()调用的。
::: hljs-center

7.png

:::

最后,在CLogHandle::Flush()方法中调用MpCommonConfigLookupDword()。
::: hljs-center

8.png

:::

CLogHandle::Flush()正是我们需要研究的对象,它负责日志文件的写入。
::: hljs-center

9.png

:::

首先,可以在(1)处看到hObject参数被传入GetFileSizeEx(),它是指向日志文件(MpCmdRun.log)的句柄。函数的返回值是是文件的大小(FileSize),这是一个LARGE_INTEGER结构。
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
} DUMMYSTRUCTNAME;
struct {
DWORD LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;

由于MpCmdRun.exe是64位可执行程序,使用联合类型的数据结构LONGLONG QuadPart来获取文件大小,其值存储在v11中。然后与MpCommonConfigLookupDword()返回的值进行比较(2)。如果文件大小大于此值,则调用PurgeLog()函数。

所以,在进行下一步操作之前,我们需要弄清楚MpCommonConfigLookupDword()的返回值。我们可以直接在调用该函数的下一条指令处下断点,通过RAX即可查看函数返回值。
::: hljs-center

10.png

:::

下图是断点命中后的状态信息:
::: hljs-center

11.png

:::

我们现在已经知道了日志文件的最大容量为0x1000000,即16777216个字节(16MB) 。

下一个待解决的问题就是当日志文件超过最大容量时,会发生什么?通过代码逻辑,我们知道当日志文件超过16MB时,会调用PurgeLog()。下面,我们进入该函数。
::: hljs-center

12.png

:::

调用此函数,会创建源文件的备份文件,如MpCmdRun.log将备份为MpCmdRun.log.bak。好极了,这确实是日志轮换机制。

我本应继续进行逆向分析,但是到了一种更加便利的方法,通过构造不同的条件来观察轮换机制的行为。

漏洞

这是我们目前所掌握的信息:
•Windows Defender将一些日志事件写入C:WindowsTempMpCmdRun.log(例如:签名更新)。
•任何用户都可以在C:WindowsTemp中创建文件和目录。
•当日志大小超过16MB时,会在C: WindowsTemp目录下,创建日志备份,并删除原日志文件。

看到这里,我不禁产生了一个疑问,如果已经有一个名为MpCmdRun.log.bak在C: WindowsTemp,又会发生什么有趣的事情呢?

我采用以下方法进行测试:
1.在C: WindowsTemp目录下创建名为MpCmdRun.log.bak的备份文件。
2.向C: WindowsTempMpCmdRun.log文件填充任意数据,使其接近16MB
3.触发签名更新机制
4.用Procmon观察结果

正常情况下MpCmdRun.log.bak会被覆盖,但是我又想到了一个新的问题,如果MpCmdRun.log.bak是目录呢?

我最初的假设是日志轮换机制会失败,但是通过Procmon的观察,我发现Defender删除了该目录。然后继续进行正常的日志轮转。而且,我在 C:WindowsTemp中同时创建了名为MpCmdRun.log.bak的文件和目录,删除竟然是递归的。

既然如此,可以重定向MpCmdRun.log.bak文件吗?

以下是测试示例:
•创建一个虚拟目标目录:C:ZZ_SANDBOXtarget
•创建MpCmdRun.log.bak目录,并设置为挂载点指向target
•向MpCmdRun.log.bak中填充16,777,002字节的任意数据
::: hljs-center

13.png

:::

挂载点的目标目录中包含一个文件夹和一个文件。
::: hljs-center

14.png

:::

下面是我在执行Update MpSignature PowerShell命令后在Procmon中观察到的情况:
::: hljs-center

15.png

:::

Defender触发日志轮换机制,递归删除了每个文件和文件夹。正不正是我们所期望的吗?作为普通用户我们可以通过该服务的日志轮换机制删除系统上任意文件或文件夹。

漏洞利用

漏洞利用方式也非常简单,只需要创建C:WindowsTempMpCmdRun.log.bak目录,并将其设置为系统上另一个位置的挂载点即可。
注意:漏洞在实际执行时,还需要一些额外的技巧才能实现删除目标文件或目录的目的,在此不再赘述。

但是,我们还会面临一个问题,大概需要多长时间才能将日志填充超过16MB呢?这是一个很重要的指标,而且16MB对于简单的日志文件已经足够大了。而且有一点还需要注意,Update-MpSignature命令不能并发执行。因为我进行了几次测试,大概估算了运行时间。

测试#1

第一个测试我采取了最简单粗暴的方法,直接运行了100次Update-MpSignature命令,记录所需时间。
::: hljs-center

16.png

:::
::: hljs-center

17.png

:::

遗憾的是,如果采用命令循环执行的方式,我们需要花费超过22个小时才能触发漏洞。
|描述|时间|文件大小|次数|
|-|-|-|-|
|Raw data for 100 calls|650s (10m 50s)|136,230 bytes|100|
|Estimated time to reach the target file size|80,050s (22h 14m 10s)|16,777,216 bytes|12,316|

通过这种方式触发漏洞,这显然是不现实的。

测试#2

在测试#1后,我仔细阅读了Update-MpSignature命令的文档,查找是否可以对命令进行优化,其中的一个参数引起了我的注意。
::: hljs-center

18.png

:::

Update-MpSignature命令接受UpdateSource参数,不幸的是当我进行测试时,大多数情况将立即返回错误信息,而且未向日志文件写入信息,这看起来是无用的。

但在我使用InternalDefinitionUpdateServer值进行测试时,发现了一个有趣的现象。
::: hljs-center

19.png

:::

因为我的VM是在windows上独立安装的,未配置”使用内部服务器”进行更新,而是使用MS服务器接收更新,因为会出现报错信息。

这个参数会立即返回错误信息,但事件仍会被写入日志文件,我们可以通过特殊的构造来利用这一点。
::: hljs-center

20.png

:::

我同样运行了该命令100次,观察执行结果:
::: hljs-center

21.png

:::
执行100次命令仅仅花费了不足4秒,这并不足以说明什么,我又测试了1000次命令执行所需时间。
::: hljs-center

22.png

:::

这是第二次测试的结果。
|描述|时间|文件大小|次数|
|-|-|-|-|
|Raw data for 1000 calls|363s (6m 2s)|2,441,120 bytes|10,000|
|Estimated time to reach the target file size|2,495s (41m 35s)|16,777,216 bytes|68,728|

以这种方式执行的话,整个填充过程大概只需要花费40分钟,这比第一次测试的22小时有了很大的进步。注意,此时是在日志文件为空的情况下进行测试的。

这个结果是可以接受的,下面是完整的测试结果:
::: hljs-center

23.png

:::

整个POC运行了38分钟,与我之前测试结果非常接近。

顺便提一下,我之所以选择C:ProgramDataMicrosoftWindowsWER这个路径,是因为一旦删除了该文件夹,您就可以通过@jonaslyk的文章,与其他漏洞配合使用拓展攻击面。

结论

这可能是我最后一篇关于特权文件滥用漏洞的文章了。在Microsoft给漏洞研究人员发送的电子邮件中,宣布不再接受此类漏洞。并称一个通用的补丁正在开发中,可以彻底解决此类问题。

链接与资源

•CVE-2020-1170 | Microsoft Windows Defender特权提升漏洞
https://portal.msrc.microsoft.com/zh-cn/security-guidance/advisory/CVE-2020-1170

•从目录删除到SYSTEM Shell
https://secret.club/2020/04/23/directory-deletion-shell.html

相关推荐: Java代码审计之跨站脚本攻击

关于跨站脚本攻击   跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script…