CVE-2020-0863:Windows Diagnostic Tracking Service任意文件读取漏洞分析

阅读量    394257 | 评论 1   稿费 200

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

0x00 前言

虽然这个漏洞不能直接实现完整权限提升,不能以NT AUTHORITY\SYSTEM权限执行代码,但由于利用过程中涉及一些”小技巧“,因此还是比较有趣。Diagnostic Tracking Service(诊断跟踪服务,也称为Connected User Experiences and Telemetry Service)可能是比较有争议的一个Windows功能,该功能用来收集用户和系统数据。我在该服务中发现了一个信息泄露漏洞(这件事情本身就有点讽刺意味),利用该漏洞,本地用户可以在NT AUTHORITY\SYSTEM上下文中读取任意文件。

 

0x01 DiagTrack RPC接口

这里我们将重点从COM转到RPC(Remote Procedure Call,远程过程调用)上,我们可以使用RpcView查看Diagtrack提供的接口。

该服务公开了多个接口,这里我们重点关注的是UUID为4c9dbf19-d39e-4bb9-90ee-8f7179b20283的接口,该接口有37个方法,因此可能也存在更大的攻击面。

我找到的漏洞位于UtcApi_DownloadLatestSetting方法中。

 

0x02 UtcApi_DownloadLatestSettings方法

RpcView可以帮我们生成RPC接口对应的IDL(Interface Definition Language,接口定义语言)文件。经过编译后,我们可知UtcApi_DownloadLatestSettings对应的C函数原型如下所示:

long DownloadLatestSettings( 
    /* [in] */ handle_t IDL_handle,
    /* [in] */ long arg_1,
    /* [in] */ long arg_2
)

该函数第一个参数为RPC绑定句柄,剩下两个参数目前尚未澄清。

如果大家不熟悉RPC接口的工作方式,这里简单介绍一下。在处理远程过程调用时,我们首先要使用远程接口对应的唯一标识符(这里为4c9dbf19-d39e-4bb9-90ee-8f7179b20283)获取该接口对应的句柄。随后,我们可以使用该句柄来调用相应的方法。因此,远程方法的第1个参数通常为一个handle_t参数。这也是大部分接口的工作方式。

获得远程接口的绑定句柄后,我首先尝试使用如下参数调用该函数:

RPC_BINDING_HANDLE g_hBinding;
/* ... initialization of the binding handle skipped ... */
HRESULT hRes;
hRes = DownloadLatestSettings(g_hBinding, 1, 1);

然后我使用Process Monitor观察后台进行的文件操作:

虽然这个服务运行在NT AUTHORITY\SYSTEM上下文中,我注意到该服务会尝试枚举如下目录中的XML文件,而该目录的所有者为当前登录用户:

C:\Users\lab-user\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Tips\

我在测试环境中使用的用户为lab-user,该用户为具备标准权限的正常用户,不具备任何管理员权限。我们之所以能观察该操作,是因为目标服务调用了diagtrack.dllFindFirstFileW()

默认情况下,这似乎是一个空文件夹,因此我在其中创建了一些XML文件:

然后再次运行测试程序,可以观察到如下结果:

这一次QueryDirectory操作能成功完成,目标服务会读取file1.xml的内容(该文件为目录中第一个XML文件),将其拷贝到C:\ProgramData\Microsoft\Diagnosis\SoftLandingStage\目录中的一个新文件(文件名保持一致)。

目标服务会对其他2个文件(file2.xmlfile3.xml)执行相同的操作:

最后,服务会删除C:\ProgramData\[…]\SoftLandingStage中创建的所有XML文件。

注意:我在Procmon中创建了一个特定规则,能将DeleteFile API调用上下文中涉及到CreateFile操作高亮标出。

上图中,CreateFile操作源自于diagtrack.dll中的DeleteFileW()调用:

 

0x03 任意文件读取漏洞

服务在拷贝文件时,并没有调用MoveFileW()来实现文件移动,也没有调用CopyFileW()实现文件拷贝,并且我们不能控制目的文件夹,因此,本地攻击者无法利用该操作将任意文件移动/拷贝到任意目录。服务会读取每个文件的内容,将内容写入C:\ProgramData\[...]\SoftLandingStage\目录中的新文件。从某种角度来看,这应该是一种手动文件复制操作。

这里我们能完全控制的一个因素为源文件目录,因为该目录所有者为当前登录的用户。我们还要注意到一点,目的文件夹的读取权限为Everyone,这意味着默认情况下,Everyone组成员可以读取该目录中创建的新文件,因此我们仍然有可能滥用这种高权限文件操作。

比如,我们可以将C:\Users\lab-user\AppData\Local\Packages\[…]\Tips文件夹替换为指向对象目录(Object Directory)的一个挂载点(mountpoint),然后创建指向文件系统上任意文件的一个伪符号链接。

如果系统中存在一个备份SAM文件,我们可以创建如下符号链接,获得该文件的一个副本。

C:\Users\lab-user\AppData\Local\Packages\[…]\Tips -> \RPC Control
\RPC\Control\file1.xml -> \??\C:\Windows\Repair\SAM

从理论上讲,如果该服务尝试打开file1.xml,就会被重定向到C:\Windows\Repair\SAM。因此,服务会读取该文件内容,将其拷贝到C:\ProgramData\[…]\SoftLandingStage\file1.xml,使得本地用户能读取该内容。是不是非常简单?然而并非如此。

这里我们会面临两个问题:

1、在Tips文件夹上调用FindFirstFileW()时会失败,因为目标挂载点并不是一个“真正的”目录。

2、整个过程结束时,服务会删除C:\ProgramData\[…]\SoftLandingStage中创建的file1.xml文件。

我们可以通过另一个挂载点来解决这两个问题,其中会涉及到一些诱饵文件以及机会锁(OpLock)。

 

0x04 解决FindFirstFileW()问题

为了利用前面介绍的服务行为,我们必须找到可靠的利用方法,将文件读取操作重定向到我们设置的任意文件。然而由于服务调用了FindFirstFileW(),这里我们无法直接使用伪符号链接。

注意:Win32 FindFirstFileW()函数首先会在目标目录中枚举满足指定过滤条件的文件,但这种方式无法适用于对象目录。比如,我们可以执行dir C:\Windows命令,但无法执行dir "\RPC Control"命令。

第一个问题解决起来非常简单。我们可以不直接创建一个对象目录,而是首先创建指向实际目录的一个挂载点,该目录中包含一些诱饵文件。

首先,我们需要创建一个临时工作目录,结构如下所示:

C:\workspace
|__ file1.xml 
|__ file2.xml

然后,创建挂载点:

C:\Users\lab-user\AppData\Local\Packages\[…]\Tips -> C:\workspace

完成这些操作后,FindFirstFileW()可以执行成功,返回file1.xml。此外,如果我们在该文件上设置一个OpLock,我们可以部分控制目标服务的执行流程(因为当远程过程尝试访问该文件时会暂停执行)。

当OpLock触发时,我们可以切换挂载点,指向对象目录。因为QueryDirectory操作只会在FindFirstFileW()调用开始时执行一次,因此这种操作能执行成功。

C:\Users\lab-user\AppData\Local\Packages\[…]\Tips -> \RPC Control
\RPC Control\file2.xml -> \??\C:\users\lab-admin\desktop\secret.txt

注意:此时我们并不需要创建file1.xml的符号链接,因为目标服务已获取该文件的句柄。

因此,当服务打开C:\Users\lab-user\AppData\[…]\Tips\file2.xml时,实际上打开的是secret.txt,然后会将其内容拷贝到C:\ProgramData\[…]\SoftLandingStage\file2.xml

总结一下:我们可以诱骗服务读取我们并不拥有的一个文件,然而这里我们会涉及到第二个问题:在操作完成时,服务会删除C:\ProgramData\[…]\SoftLandingStage\file2.xml,此时我们将无法读取该文件内容。

 

0x05 解决文件删除问题

由于目标文件会在操作完成时删除,因此我们必须赢得与目标服务的竞争条件,在服务执行删除操作前拿到文件的副本。为了完成该任务,我们可以有两种选择:第一个选择为采取暴力方式。我们可以实现一个监控机制,循环监控C:\ProgramData\[…]\SoftLandingStage目录文件夹,当NT AUTHORITY\SYSTEM完成新XML文件写入时,第一时间拿到文件副本。

然而,暴力方式并不是最佳选择。这里我们有更为可靠的第二种选择,但我们得从头考虑整个策略。

前面我们在临时的工作目录中创建了2个文件,这里我们要创建3个文件:

C:\workspace
|__ file1.xml
|__ file2.xml  
|__ file3.xml

下一个步骤相同。然而,当file1.xml上的OpLock触发时,我们将多执行两个操作。

首先,我们将切换挂载点,创建2个伪符号链接。然后,我们要确保file3.xml链接指向的是实际的file3.xml文件:

C:\Users\lab-user\AppData\Local\Packages\[…]\Tips -> \RPC Control
\RPC Control\file2.xml -> \??\C:\users\lab-admin\desktop\secret.txt
\RPC Control\file3.xml -> \??\C:\workspace\file3.xml

然后,在释放第一个OpLock前,我们需要在file3.xml上设置一个新的OpLock。

采用这种策略后,目标服务的整个操作过程如下所示:

1、DiagTrack尝试读取file1.xml,触发第一个OpLock。

2、此时,我们切换挂载点,创建2个符号链接,在file3.xml上设置OpLock。

3、释放第一个OpLock(file1.xml)。

4、DiagTrack拷贝file1.xmlfile2.xmlfile2xml指向的是secret.txt)。

5、DiagTrack尝试读取file3.xml,触发第二个OpLock。

6、这一步为关键步骤。此时,远程过程被暂停,因此我们可以拿到C:\ProgramData\[…]\SoftLandingStage\file2.xml的副本,该文件本身就是secret.txt的副本。

7、释放第二个OpLock(file3.xml)。

8、远程过程结束,删除3个XML文件。

注意:这种技巧之所以行之有效,是因为DiagTrack采用顺序执行方式来操作整个过程,每个文件会被依次拷贝,最后删除新创建的所有文件。

这种方式较为可靠,使普通用户能够获得NT AUTHORITY\SYSTEM能读取的任意文件的副本。我开发的PoC测试结果如下图所示:

 

0x06 参考资料

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多