Windows注册表漏洞实战利用:内存破坏与权限提升

本文深入探讨Windows注册表内存破坏漏洞的实战利用技术,涵盖UAF漏洞利用、越界单元格索引、内核对象操纵等高级技巧,详细分析如何从普通用户权限提升至系统级权限的完整攻击链。

Windows注册表冒险 #8:注册表内存破坏的实战利用

作者:Mateusz Jurczyk,Google Project Zero

在之前的博客文章中,我们重点讨论了注册表的一般安全分析以及如何有效寻找其中的漏洞。在这里,我们将把注意力转向基于注册表配置单元(hive)的内存破坏漏洞的利用,即那些允许攻击者覆盖内存中活动注册表配置单元映射内数据的漏洞。这是Windows注册表特有的一类问题,但也具有足够的通用性,使得本文描述的技术适用于我过去的17个漏洞,以及未来可能出现的任何类似漏洞。

正如我们所知,注册表配置单元在低级内存管理(如何以及在何处映射到内存中)、自定义分配器处理已分配和已释放内存块的方式以及存储在那里的数据性质方面表现出非常特殊的行为。所有这些使得从攻击性安全的角度来看,利用这类漏洞特别有趣,这也是我想在这里详细描述的原因。

漏洞分类

与任何其他类型的内存破坏一样,绝大多数注册表配置单元内存破坏问题可以分为两类:空间违规(如缓冲区溢出)和时间违规(如释放后使用)。

在本文中,我们将致力于选择最有希望的漏洞候选者,然后为其创建一个分步利用程序,将系统中普通用户的权限从中等完整性级别(Medium IL)提升到系统级权限。我们的目标将是Windows 11,还有一个额外要求是成功绕过所有现代安全缓解措施。

利用起点:潜在选项的高级概述

让我们从回顾一些关键点开始。正如您可能还记得的那样,Windows注册表单元分配器(即内部的HvAllocateCell、HvReallocateCell和HvFreeCell函数)的操作方式非常有利于利用。首先,它完全缺乏任何针对内存破坏的防护措施;其次,它没有任何随机性元素,使其行为完全可预测。因此,不需要采用任何"注册表配置单元喷洒"或从典型堆利用中已知的其他类似技术——如果我们设法在测试机器上实现所需的单元布局,它将在其他计算机上无需任何额外步骤即可重现。

经典内存破坏漏洞的利用通常涉及以下步骤:

  1. 初始内存破坏原语
  2. ???
  3. 利润(以任意代码执行、权限提升等形式)

利用开发人员的任务是填补此列表中的空白,设计出通向期望目标的中间步骤。通常有几个这样的中间步骤,因为考虑到当前的安全状态和缓解措施,漏洞很少能一步到位地从内存破坏直接导致代码执行。

注册表配置单元内破坏

让我们从调查覆盖内部注册表配置单元数据是否像最初看起来那样不切实际开始。

在特权系统注册表配置单元中执行仅限注册表配置单元的攻击

需要明确的是,说注册表配置单元不包含任何值得覆盖的数据并不完全准确。如果您仔细想想,情况恰恰相反——注册表存储了大量的系统配置、注册服务信息、用户密码等。唯一的问题是,所有这些关键数据都位于特定的注册表配置单元中,即那些挂载在HKEY_LOCAL_MACHINE下的注册表配置单元,以及HKEY_USERS中的一些注册表配置单元(例如,HKU.Default,对应于System用户的私有注册表配置单元)。

要能够通过仅破坏regf格式数据(而不访问其他内核内存或实现任意代码执行)成功执行攻击并提升权限,必须满足两个条件:

  1. 漏洞必须仅通过API/系统调用即可触发,并且不需要对注册表配置单元的二进制控制,因为我们显然对任何系统注册表配置单元都没有这种控制。
  2. 目标注册表配置单元必须包含至少一个具有足够宽松访问权限的键,允许非特权用户创建值(KEY_SET_VALUE权限)和/或新的子键(KEY_CREATE_SUB_KEY)。

滥用regf不一致性触发内核池破坏

虽然内存中的注册表配置单元映射在某种程度上是隔离和自包含的,但它们并非存在于真空中。Windows内核在内核池空间中分配和管理许多与注册表相关的附加对象。这些对象通过数据缓存实现优化,并帮助实现仅通过对注册表配置单元空间的操作无法实现的某些功能(例如,事务、分层键)。

对于潜在攻击者来说,这意味着注册表配置单元内存破坏可能升级为某种形式的池破坏。这为利用提供了更广泛的选择,因为内核的各个部分使用了各种池分配。

注册表配置单元间内存破坏

到目前为止,在我们的分析中,我们假设使用基于注册表配置单元的内存破坏漏洞,我们只能修改我们正在操作的特定注册表配置单元内的数据。然而,在实践中,情况不一定如此,因为可能有其他数据位于我们的注册表配置单元映射附近的内存中。

注册表进程用户空间中的其他注册表配置单元映射

在注册表进程的用户空间中映射注册表配置单元的节视图是绝大多数注册表的默认行为。内存中各个映射的布局可以很容易地从WinDbg中观察到。

攻击基于池的注册表配置单元映射中的相邻内存

尽管注册表配置单元文件视图通常映射在注册表进程的用户模式空间(其中除了这些映射之外没有其他内容),但在少数情况下,这些数据直接存储在内核模式池中。

终极原语:越界单元格索引

情况显然不像之前看起来那样无望,并且有相当多的方法可以将自己注册表配置单元空间中的内存破坏转化为对其他类型内存的控制。

作为快速提醒,单元格索引是注册表配置单元中等效于指针的东西:它们是32位值,允许分配的单元相互引用。使用称为单元格映射的特殊3级结构将单元格索引转换为其对应的虚拟地址,该结构类似于CPU页表。

从利用的角度来看,这里有两个关键事实。首先,HvpGetCellPaged函数不对输入索引执行任何边界检查。其次,对于小于2 MiB的注册表配置单元,Windows应用了一个称为"small dir"的额外优化。

编写利用程序

在这个阶段,我们已经有了一个可靠的计划,说明如何利用初始的注册表配置单元内存破坏原语进行进一步的权限提升。是时候选择一个特定的漏洞并开始为其编写实际的利用程序了。

步骤0:选择漏洞

面对大约17个与注册表配置单元内存破坏相关的漏洞,直接的挑战是选择一个用于演示的利用程序。

CVE-2022-34707

在注册表上下文中,我首先想到的漏洞总是CVE-2022-34707。这部分是因为它是我在这项研究中手动发现的第一个错误,但主要是因为利用起来非常方便。

CVE-2023-23420

这使我们来到第二个漏洞,CVE-2023-23420。这也是注册表配置单元内的UAF条件,但它涉及的是键节点单元而不是安全描述符单元。

步骤1:滥用UAF建立动态控制的值单元

让我们首先澄清,我们的攻击将针对HKCU注册表配置单元,更具体地说是其易失性存储空间。

步骤2:获取对CMHIVE内核对象的读/写访问权限

由于我们现在完全控制了一些注册表值,下一个逻辑步骤是将它们初始化为特殊构造的OOB单元格索引,然后检查我们是否实际上可以访问它代表的内核结构。

步骤3:获取对整个内核地址空间的任意读/写访问权限

此时,我们可以通过我们的魔法值从_CMHIVE结构读取和写入,也可以操作解析为有效地址的任何其他越界单元格索引。

步骤4:提升进程安全令牌

拥有任意内核读/写原语和ntoskrnl.exe的地址后,提升权限只是一种形式。

最终思考

既然我们已经完全实现了预期目标,我们可以回到之前不完整的图表,并用我们采取的所有中间步骤填充它:

利用缓解措施

上述利用表明,注册表中的越界单元格索引是一种强大的利用技术,其主要优势在于其确定性。

虚假文件不可变性

到目前为止,在这个博客中,我们主要关注的是我们可以通过内存破坏控制活动注册表配置单元的内部regf数据的情况。

结论

亲爱的读者,如果您已经阅读到这篇博客文章的结尾,特别是如果您已经阅读了这个系列的所有文章,我想衷心祝贺您的毅力。🙂 通过这些文章,我希望我设法记录了尽可能多的注册表实现细节;这些细节可能永远不会重见天日。我的目标是展示这个机制是多么有趣和内部复杂,特别是它在Windows整体安全中扮演的重要角色。感谢您加入我这次冒险,下次见!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计