这篇文章由Raik Schneider撰写,披露了Microsoft PowerToys中的DLL劫持(DLL Hijacking)漏洞。文章详细描述了Schneider在2025年2月发现的这一安全问题,指出PowerToys中的ZoomIt工具因不安全的DLL加载机制,允许攻击者在工作目录中植入恶意DLL文件,从而执行恶意代码。Schneider通过将修改过的TextShaping.dll置于PowerToys安装目录,成功触发了Windows计算器(calc.exe)的运行,证明了漏洞的存在。文章分析了不同安装方式(如网络驱动器、系统范围或用户特定安装)对漏洞利用的影响,并指出攻击可能需要通过社会工程或直接访问来实现。Schneider还提到,尽管他向微软报告了该问题,但微软认为这仅是“深度防御”问题,未予高优先级处理。文章提供了详细的技术分析、视频演示以及对用户和管理员的建议,强调使用安全解决方案以确保加载可信DLL。
早在 2025 年 2 月,我就发现并披露了 Sysinternals 工具中的一个漏洞 ,该漏洞允许应用程序从工作目录不安全地加载 DLL。您可以在此处找到完整的分析和披露。
随着 2025 年 1 月 29 日 Sysinternals 博客宣布流行工具 ZoomIt将来将成为Microsoft PowerToys 的一部分 ,显然需要检查此漏洞是否会在新环境中持续存在。不幸的是,这一消息已被证实。
🔍 什么是 DLL 劫持?
DLL 劫持(也称为 DLL 植入)是一种安全漏洞,攻击者将精心设计的 DLL 文件放置在应用程序的工作目录或其他加载目录中。如果 DLL 在没有安全路径的情况下加载,就会导致恶意代码的执行。
当程序以提升的权限运行时,这一点尤其重要——系统工具、安装程序助手或自动化工具通常就是这种情况。
⚙️ PowerToys 的差距
在 Windows 11 VM 上的测试中,我能够证明 PowerToys 工具(例如 ZoomIt 和 Text Extractor)从工作目录加载精心设计的 DLL - 无需指定路径,也无需检查签名。
我将自定义的 TextShaping.dll 放入 PowerToys 安装目录中,加载时只需打开计算器(calc.exe)。当 ZoomIt 或文本提取器启动时,计算机实际上就启动了——这是不安全的 DLL 加载行为的明显证据。
📁 安装:三种方案,一个问题
该行为很大程度上取决于 PowerToys 的安装方式:
-
网络驱动器/便携版本: 程序也可以通过网络驱动器运行——攻击在那里特别容易
-
系统范围安装(机器范围): 通过管理员权限进行写保护 - 攻击更困难,但并非不可能
-
用户特定安装(每个用户): AppData 目录完全可写 -易于利用
因此,漏洞利用取决于用户是否可以通过社会工程学将相关文件复制到工作目录中,或者攻击者自己是否可以将文件复制到工作目录中。将文件“复制”到可写目录(类似于“网络驱动器”场景)也是一种潜在的攻击媒介。
🔬 技术分析:代码中的错误
在官方 PowerToys GitHub 存储库中, dll.c文件 负责加载 DLL。函数 LoadLibrarySafe() 检查是否支持扩展加载标志 - 但 如果不支持,则在没有任何保护的情况下加载 DLL :
有漏洞的代码部分:
//=========================================================================-==//// LoadLibrarySafe//// Loads a DLL from the system folder in a way that mitigates DLL spoofing /// side-loading attacks////============================================================================HMODULE LoadLibrarySafe(LPCTSTR libraryName, DLL_LOAD_LOCATION location){ HMODULE hMod = NULL;if (NULL == libraryName || location <= DLL_LOAD_LOCATION_MIN || location >= DLL_LOAD_LOCATION_MAX) { SetLastError(ERROR_INVALID_PARAMETER);returnNULL; }// LOAD_LIBRARY_SEARCH_SYSTEM32 is only supported on Window 7 or later. On earlier SKUs we could use a fully// qualified path to the system folder but specifying a path causes Ldr to skip SxS file redirection. This can // cause the wrong library to be loaded if the application is using a manifest that defines a specific version // of Microsoft.Windows.Common-Controls when loading comctl32.dllif (DLL_LOAD_LOCATION_SYSTEM == location) { DWORD flags = ExtendedFlagsSupported() ? LOAD_LIBRARY_SEARCH_SYSTEM32 : 0; hMod = LoadLibraryEx(libraryName, NULL, flags); }return hMod;}
原始代码根据操作系统检查是否支持扩展安全标志(LOAD_LIBRARY_SEARCH_SYSTEM32)。如果 没有,则 DLL 不受任何限制地加载 。
这意味着:
如果操作系统 太旧 或者保护机制不可用,则会 调用不带标志的LoadLibraryEx 。这正是问题所在:
如果不添加额外的标志,Windows 将使用经典的 DLL 搜索顺序 - 包括 当前工作目录,这 可能会被滥用于 DLL 劫持或植入攻击。
这种行为在新代码中得到了一致的保障。仅当 LOAD_LIBRARY_SEARCH_SYSTEM32 可用时才会加载 - 否则该函数会故意因错误而终止。
修改后的代码:
//=========================================================================-==//// LoadLibrarySafe//// Loads a DLL from the system folder in a way that mitigates DLL spoofing /// side-loading attacks////============================================================================HMODULE LoadLibrarySafe(LPCTSTR libraryName, DLL_LOAD_LOCATION location){ HMODULE hMod = NULL;// Nur genau definierter Parameter erlaubtif (libraryName == NULL || location != DLL_LOAD_LOCATION_SYSTEM) { SetLastError(ERROR_INVALID_PARAMETER);returnNULL; }// Nur auf unterstützten Systemen DLL sicher aus System32 ladenif (ExtendedFlagsSupported()) { hMod = LoadLibraryEx(libraryName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); } else {// Unsicheres Fallback verhindern SetLastError(ERROR_NOT_SUPPORTED); hMod = NULL; }return hMod;}
这可以防止任何不安全的执行。这是更安全的,因为:
-
不会自动回退到不安全的搜索路径。
-
仅允许明确允许的加载位置(System32)。
-
不明确或被操纵的枚举值被排除。
在安全关键型组件中(例如 PowerToys 中或之前的 Sysinternals 中),加载器绝 不能允许不安全的路径 - 即使“出于兼容性原因”。现代系统支持 LOAD_LIBRARY_SEARCH_SYSTEM32,在旧系统上,受控终止 比潜在的漏洞更好
🪓 微软的评估
该漏洞已及时 报告给微软安全响应中心(MSRC) 。答案是——就像 Sysinternals 漏洞一样—— 不存在传统意义上的安全风险。
⚖️ 与类似案例的比较和评估
脆弱性 | 工具/组件 | MSRC 分类 |
---|---|---|
Sysinternals 中的 DLL 劫持 | ZoomIt、Autoruns 等。 | 低的 |
PowerToys 中的 DLL 劫持 | ZoomIt,文本提取器 | 低的 |
计算器(calc.exe)中的 DLL 漏洞 | Windows 组件 | 低的 |
RDP 凭证缓存设计缺陷 | 远程桌面服务 | 低的 |
这种持续的低评级表明, 风险评估的系统方法从技术角度来看是值得怀疑的——特别是如果漏洞被视为 攻击链的一部分 (例如,在通过社会工程学执行初始代码之后)。
包括安全社区中的许多人都 在怀疑这个漏洞是否应该被归类为严重漏洞——我想就此出现的一些争论做出回应。
一些声音声称:
-
“这是一种理论上的攻击,因为攻击者必须能够访问本地系统。”
-
“在现代安装中,程序在普通用户目录中受到保护。”
-
“利用此漏洞需要对系统进行物理访问或具有管理权限。”
乍一看,这似乎是 合理的 ——毕竟漏洞是在 工作目录中被利用的,而在许多情况下,工作目录是受保护的区域。但仔细观察就会发现,这种做法 太过短视 :
-
访问工作目录对于攻击者来说通常不是什么障碍。在AppData 安装 或 网络驱动器 等场景中 , 程序没有受到保护,无法免受 DLL 的操纵。用户甚至可以轻松地将文件放置在程序目录中并执行它们。
-
操纵 DLL 是一种众所周知的攻击场景。 即使攻击者无法直接远程访问系统,该漏洞与其他漏洞结合,也会导致 权限提升 或 将恶意软件引入 系统。这使得差距变得非常重要。
-
我已经展示了如何通过在程序目录中放置一个可操作的 DLL(例如 TextShaping.dll)来启动 Windows 计算器。这个 概念证明 表明该场景可以在实践中被利用——即使没有管理员权限。
值得注意的是,微软的风险评估通常是 考虑到被利用的可能性的保守估计 。 但 现实的威胁情况 往往有所不同。 DLL 加载器中允许通过不受保护的工作目录进行操作的漏洞仍然是攻击者的重要目标,尤其是与其他漏洞结合使用时。例如,在强化措施阻止执行白名单之外的应用程序的环境中,您自己的代码可以通过 DLL 在受信任的、经过验证的应用程序的上下文中执行。
在这种情况下,不仅要从技术角度评估其 严重性,还要从如何在攻击链中利用此漏洞的角度进行评估。在许多现代攻击中,此类漏洞是 系统不断受到攻击的第一步。
🧵 结论
此漏洞表明,即使是像 PowerToys 这样的现代开源项目也存在数十年的遗留问题,或者直接从 Sysinternals 继承这些问题。尽管微软淡化了风险,但现实依然存在: 正确目录中的简单 DLL 足以执行任意代码- 即使在主动阻止非白名单应用程序执行的强化系统上也是如此。
原文始发于微信公众号(Ots安全):Microsoft PowerToys 中的 DLL 劫持
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论