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.dll
中FindFirstFileW()
:
默认情况下,这似乎是一个空文件夹,因此我在其中创建了一些XML文件:
然后再次运行测试程序,可以观察到如下结果:
这一次QueryDirectory
操作能成功完成,目标服务会读取file1.xml
的内容(该文件为目录中第一个XML文件),将其拷贝到C:\ProgramData\Microsoft\Diagnosis\SoftLandingStage\
目录中的一个新文件(文件名保持一致)。
目标服务会对其他2个文件(file2.xml
、file3.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.xml
及file2.xml
(file2xml
指向的是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 参考资料
- CVE-2020-0863 – Connected User Experiences and Telemetry Service Information Disclosure Vulnerability
https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-0863 - My PoC for CVE-2020-0863
https://github.com/itm4n/DiagTrackAribtraryFileRead - RpcView
https://www.rpcview.org/ - Symbolic Link Testing Tools – James Forshaw
https://github.com/googleprojectzero/symboliclink-testing-tools
发表评论
您还未登录,请先登录。
登录