概述
在本篇文章中,我们将研究Windows的“已连接的用户体验和遥测”功能,该功能也被称为“diagtrack”。本文将大量涉及与NTFS相关的术语,因此需要读者首先有一定的相关知识背景。
在反馈中心中,有一个名为“高级诊断”的功能引起了我的关注。这个功能可以由所有用户触发,并且会导致C:\Windows\Temp中的文件活动,该目录可以由所有用户执行写入操作。
要对这个功能进行逆向工程并复现所需的交互是一个很大的挑战,因为它使用的是WinRT IPC,而不是COM,并且我并不是很了解WinRT的存在,因此在此之前还有一些准备事项要做。
在C:\Program Files\WindowsApps\Microsoft.WindowsFeedbackHub_1.2003.1312.0_x64__8wekyb3d8bbwe\Helper.dll中,我发现了一个值得关注的函数:
WINRT_IMPL_AUTO(void) StartCustomTrace(param::hstring const& customTraceProfile) const;
该函数将执行WindowsPerformanceRecorder配置文件,该配置文件在XML文件中定义,而XML文件则被指定为Diagtrack Service安全上下文中的参数。
这个文件路径是相对于System32文件夹进行解析的,因此,我将一个XML文件放在了所有用户可以写入的System32\Spool\Drivers\Color中,并传递了该目录相对于前面提到的系统目录的路径。接下来,Diagtrack开始了跟踪记录。
如果我们查看最小化的 WindowsPerformanceRecorder配置文件,我们将会看到如下内容:
< WindowsPerformanceRecorder Version="1" > < Profiles > < SystemCollector Id="SystemCollector" > < BufferSize Value="256" / > < Buffers Value="4" PercentageOfTotalMemory="true" MaximumBufferSpace="128" / > < /SystemCollector > < EventCollector Id="EventCollector_DiagTrack_1e6a" Name="DiagTrack_1e6a_0" > < BufferSize Value="256" / > < Buffers Value="0.9" PercentageOfTotalMemory="true" MaximumBufferSpace="4" / > < /EventCollector > < SystemProvider Id="SystemProvider" / > < Profile Id="Performance_Desktop.Verbose.Memory" Name="Performance_Desktop" Description="exploit" LoggingMode="File" DetailLevel="Verbose"> < Collectors > < SystemCollectorId Value="SystemCollector" > < SystemProviderId Value="SystemProvider" / > < /SystemCollectorId > < EventCollectorId Value="EventCollector_DiagTrack_1e6a" > < EventProviders > < EventProviderId Value="EventProvider_d1d93ef7" / > < /EventProviders > < /EventCollectorId > < /Collectors > < /Profile > < /Profiles >< /WindowsPerformanceRecorder>
信息泄漏问题
如果让用户具有完全控制文件的权限,可能会产生一些攻击面。 EventCollector元素的name属性用于创建记录的跟踪的文件名。文件路径为:
C:\Windows\Temp\DiagTrack_alternativeTrace\WPR_initiated_DiagTrackAlternativeLogger_DiagTrack_XXXXXX.etl
其中,XXXXXX是name属性的值。
通过将名称设置为\..\..\file.txt,可以轻松获得对下述文件名和路径的完全控制:
C:\Windows\Temp\DiagTrack_alternativeTrace\WPR_initiated_DiagTrackAlternativeLogger_DiagTrack\..\..\file.txt:.etl
这将导致C:\Windows\Temp\file.txt被使用。
SYSTEM使用FILE_OVERWRITE_IF作为配置文件打开记录的跟踪,因此可以覆盖SYSTEM有权限写入的任何文件。我们也可以在SYSTEM有权限写入的位置创建文件盒目录(通过添加::$INDEX_ALLOCATION)
从信息泄漏的角度来看,为服务执行的跟踪选择任何ETW提供程序也是非常值得关注的。
其中,我们可以在不知道文件名的情况下利用数据,因为服务可以在我们没有权限列出文件的文件夹中创建文件。
这些文件名可能会被Microsoft-Windows-Kernel-File provider泄漏,在我们的示例中,泄漏的内容位于将22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716添加到WindowsPerformanceRecorder配置文件的过程中记录的etl文件中。
0xFFFF81828C6AC858 0xFFFF81828C85E760 10096 0x1000020 0x0 0x3 \Device\HarddiskVolume2\Users\jonas\OneDrive\Dokumenter\FeedbackHub\DiagnosticLogs\Install and Update-Post-update app experience\2019-12-13T05.42.15-SingleEscalations_132206860759206518\file_14_ProgramData_USOShared_Logs__
这种信息泄漏,可能从看似无法利用的场景中,产生漏洞利用的可能性。
其他安全性绕过的提供程序包括:
Microsoft-Windows-USB-UCX {36DA592D-E43A-4E28-AF6F-4BC57C5A11E8}
Microsoft-Windows-USB-USBPORT {C88A4EF5-D048-4013-9408-E04B7DB2814A}(捕获原始USB数据,从而启用键盘记录功能)
Microsoft-Windows-WinINet {43D1A55C-76D6-4F7E-995C-64C711E5CAFE}
Microsoft-Windows-WinINet-Capture {A70FF94F-570B-4979-BA5C-E59C9FEAB61B}(捕获来自IExplorer、Microsoft Store等应用的原始HTTP流量,或者捕获预加密的SSL流)
Microsoft-PEF-WFP-MessageProvider(IPSEC VPN数据预加密)
代码执行
以上泄漏的信息已经足够了,那么我们应该如何将其转换为代码执行呢?
我们仅仅拥有控制.etl文件目标的能力,很可能无法轻易实现代码执行。在这里,寻找另一个入口点是很有必要的。对文件内容的有限控制将使漏洞利用变得非常困难。我们也许可以设计一个可执行的PowerShell脚本或bat文件,但是如何让这些脚本执行,这是一个问题。
相反,我选择将我的活动跟踪记录与以下调用结合起来:
WINRT_IMPL_AUTO(Windows::Foundation::IAsyncAction) SnapCustomTraceAsync(param::hstring const& outputDirectory)
当提供位于%WINDIR%\temp\DiagTrack_alternativeTrace内的outputDirectory值时,就会出现一种有趣的行为。
Diagtrack服务会将DiagTrack_alternativeTrace中所有创建的.etl文件重命名为SnapCustomTraceAsync的outputDirectory参数中给出的目录。这样一来,就可以获取到目标的控制权,这是由于在授予非特权用户写访问权限的文件夹中创建源文件时发生的重命名操作可以被利用。其原理在于文件对其父目录的权限继承。通过重命名操作移动文件时,DACL不会更改。这意味着,如果我们可以使目标变为%WINDIR%\System32,并以某种方式移动文件,那么我们仍然对该文件具有写入权限。因此,我们目前已经控制了SnapCustomTraceAsync的outputDirectory参数,但是存在一些限制。
如果所选的outputDirectory不是%WINDIR%\temp\DiagTrack_alternativeTrace的子目录,则不会发生重命名。outputDirectory将无法存在,因为Diagtrack服务必须要创建它。在创建时,将以SYSTEM作为该文件夹的所有者,用户仅会被授予读取权限。
这里就存在一个问题,我们无法将目录放入挂载点。即使我们拥有必需的权限,也无法通过清空目录的方式来停止这一过程,因为Diagtrack已经将快照输出的etl文件放置在该目录下。但幸运的是,我们可以通过在outputDirectory目标和DiagTrack_alternativeTrace之间创建两个层次的间接来绕过这些障碍。
我们创建文件夹DiagTrack_alternativeTrace\extra\indirections,并提供%WINDIR%\temp\DiagTrack_alternativeTrace\extra\indirections\snap作为outputDirectory,从而允许Diagtrack创建具有受限权限的快照文件夹,就像我们在DiagTrack_alternativeTrace内部一样。这样一来,我们可以重命名由我们创建的额外文件夹。由于在目录中存在被Diagtrack打开的文件,因此我们需要两个层次的间接来绕过目录的锁定。在重命名额外目录时,我们可以重新创建%WINDIR%\temp\DiagTrack_alternativeTrace\extra\indirections\snap(现在为空),并且我们将拥有对该文件夹的完整权限。
现在,我们可以将DiagTrack_alternativeTrace\extra\indirections\snap转换为以%WINDIR%\system32为目标的挂载点,Diagtrack会将所有与WPR_initiated_DiagTrack*.etl*相匹配的文件移动到%WINDIR%\system32中。在授予用户写入权限的文件夹中创建文件后,这些文件仍然是可以写入的。但遗憾的是,对System32中文件的完全控制还不足以让我们实现代码执行。也就是说,我们还需要有一种执行用户可控制的文件名的方法,例如像是James Forshaw提出的DiagnosticHub插件方法。这里必须提醒大家注意的是,DiagnosticHub现在要求其加载的所有DLL都必须经过Microsoft签名。然而我们确实有一些途径可以在SYSTEM安全上下文中执行system32中的DLL文件,但前提条件是只能执行特定的文件名。那么,现在对我们来说的另一个障碍就是文件名不可控。我们应该如何控制文件名呢?
如果我们不将挂载点设置为System32,而是以NT命名空间中的对象目录作为目标,并创建一个与重命名目标文件同名的符号链接,就可以控制文件名。符号链接的目标将成为重命名操作的目标。例如,将其设置为\??\%WINDIR%\system32\phoneinfo.dll将导致对一个错误报告服务相关的文件具有写入权限,在错误报告在进程外提交时,错误报告服务会加载并执行该文件。于是,我将挂载点重新定为了\RPC Control,因为它允许所有用户在其中创建符号链接。
我们可以试试!
当Diagtrack应该对其重命名时,实际上什么都没发生。这是因为在完成重命名操作之前,目标文件夹就已经打开,但现在该文件夹是一个对象目录。因此,无法通过文件或目录的API调用将其打开。通过将创建挂载点的时间设置为打开文件夹之后、重命名文件夹之前的时间,可以避免这种情况。通常,我会在目标文件夹中创建一个与目标文件同名的文件。然后,我在文件上设置一个机会锁(Oplock),当机会锁中断时,我就知道已经完成了文件夹检查,并且将要进行重命名操作。在释放锁之前,我将文件移动到另一个文件夹,然后在现在已经为空的文件夹上设置挂载点。由于已经将重命名操作配置为不会覆盖已经存在的文件,因此这个技巧将不起作用。这也就意味着,重命名将由于已经存在的文件而终止,不会触发机会锁。
在临近放弃的边缘,我意识到了一些事情:
如果我每隔1毫秒,就将合法文件夹与对象目录之间的交接点(Junction Point)进行切换,那么在文件夹检查完成后,就有50%的可能性获得合法目录,而在重命名发生时,有50%的概率获得对象目录。这样一相乘,我们就有25%的概率可以重命名并通过验证,但最终会在System32中显示为phoneinfo.dll。如果可能,我会尽可能避免产生竞争条件的情况,但在这种场景中,似乎没有其他可行的方法,我可以通过不断重复这一过程来增加成功利用的概率。为了应对可能会失败的情况,我决定触发任意数量的重命名。幸运的是,有一个关于流的详情可以让我们触发尽可能多的重命名操作。重命名并没有链接到诊断服务知道的已创建文件,因此在这里唯一的需求就是使其位于%WINDIR%\temp\DiagTrack_alternativeTrace中,并且符合WPR_initiated_DiagTrack*.etl*的形式。
由于我们有权限在目标文件夹中创建文件,因此我们现在可以创建WPR_initiated_DiagTrack0.etl、WPR_initiated_DiagTrack1.etl等等,它们都将会被重命名。
由于最终目标是在System32文件夹中的phoneinfo.dll文件,那么我们实际上可以将文件创建为指向预期Payload的硬链接。这样一来,在移动后,就无需使用写入权限覆盖文件了。
经过一些尝试后,我得到了以下的解决方案:
1、创建文件夹%WINDIR%\temp\DiagTrack_alternativeTrace\extra\indirections;
2、开始诊断跟踪:
%WINDIR%\temp\DiagTrack_alternativeTrace\WPR_initiated_DiagTrackAlternativeLogger_WPR System Collector.etl已创建;
3、创建%WINDIR%\temp\DiagTrack_alternativeTrace\WPR_initiated_DiagTrack[0-100].etl,作为指向Payload的硬链接;
4、创建符号链接\RPC Control\WPR_initiated_DiagTrack[0-100.]etl,指向%WINDIR%\system32\phoneinfo.dll;
5、在WPR_initiated_DiagTrack100.etl上设置机会锁,在中断时,检查%WINDIR%\system32\phoneinfo.dll是否存在,如果不存在,则重复创建WPR_initiated_DiagTrack[].etl文件和对应的符号链接;
6、在WPR_initiated_DiagTrack0.etl上打开机会锁,当它中断时,我们就知道重命名流已经开始,但第一次重命名操作还尚未进行。
在中断时:
1、将%WINDIR%\temp\DiagTrack_alternativeTrace\extra重命名为%WINDIR%\temp\DiagTrack_alternativeTrace\{RANDOM-GUID};
2、创建文件夹%WINDIR%\temp\DiagTrack_alternativeTrace\extra\indirections\snap;
3、启动线程,循环切换%WINDIR%\temp\DiagTrack_alternativeTrace\extra\indirections\snap作为%WINDIR%\temp\DiagTrack_alternativeTrace\extra或NT对象命名空间中\RPC Control的挂载点;
4、使用%WINDIR%\temp\DiagTrack_alternativeTrace\extra\indirections\snap作为输出目录,启动快照跟踪。
在执行后,将重命名100个文件。如果它们最终都没有变成system32中的phoneinfo.dll,那么还会重复执行,直到成功为止。
然后,我在切换交接点的线程中添加了对%WINDIR%\system32\phoneinfo.dll是否存在的检查。在两次切换之间增加的延迟似乎增加了成功的概率。经过测试,我们发现基本可以在前100次循环结束前完成任务。
在检测到%WINDIR%\system32\phoneinfo.dll时,将会提交给Windows错误报告服务一份空白的报告,该错误报告服务配置为从进程外提交,从而导致wermgmr.exe在SYSTEM安全上下文中加载刚刚创建的phoneinfo.dll。
我们的Payload是一个DLL,在DLL_PROCESS_ATTACH上它将检查SeImpersonatePrivilege是否启用,如果启用,则cmd.exe将会在当前的活动桌面上产生。如果没有通过特权检查,则会启动其他命令提示符,因为启动错误报告的进程也会尝试加载phoneinfo.dll。
此外,在此过程中,还会使用WTSSendMessage显示出一条信息。所以即使我们无法在正确的会话/桌面上产生命令提示符,我们也可以获得成功的指示。
这里之所以显示为红色,是因为我的命令提示符自动执行了echo test> C:\windows:stream && color 4E;,这样就可以让所有UAC提升后的命令提示符的背景颜色为红色,从而方便区分。
尽管在我的项目中还包含私有库,但它可能有助于大家研究其工作原理:
https://github.com/thesecretclub/diagtrack/blob/master/example.cpp
本文翻译自:https://secret.club/2020/07/01/diagtrack.html如若转载,请注明原文地址: