CVE-2024-26230: Windows Telephony Service - 存在呼叫问题(权限提升)
执行摘要
CVE-2024-26230是Windows Telephony Service(TapiSrv)中发现的一个关键漏洞,可导致受影响系统上的权限提升。该漏洞利用了FreeDialogInstance中的释放后使用(use-after-free)漏洞。通过操纵注册表,攻击者可以控制内存分配来创建伪造对象,触发TUISPIDLLCallback中的UAF以获取代码执行权限。这进一步与绕过CFG等缓解技术结合,最终加载恶意DLL,通过PrintSpoofer将权限提升至SYSTEM。在本博客文章中,我们将深入探讨此漏洞的工作原理、如何利用以及可帮助防御的缓解策略。
漏洞概述
受影响产品
| 产品 | Windows Telephony Service (TapiSrv) |
|---|---|
| 厂商 | Microsoft |
| 安全影响 | 权限提升 |
| CVE ID | CVE-2024-26230 |
| CWE(s) | CWE-416 - 释放后使用 |
CVSS3.1评分
基础评分:7.8
向量字符串:CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
受影响软件
版本:Microsoft Windows 10 Version 22H2 Build 19045.3803 AMD64
漏洞描述
漏洞存在于C:\Windows\System32\tapisrv.dll中的FreeDialogInstance函数中。具体来说,当函数释放GOLD对象(由魔术数字0x474F4C44标识)时,原始上下文句柄仍保留指向它的指针,导致释放后使用条件。
以下是此错误发生的主要原因以及如何利用:
- FreeDialogInstance函数释放GOLD对象,但未确保上下文句柄对象不再引用它。此问题在第1行和第2行都很明显。
- TUISPIDLLCallBack函数尝试使用已释放的GOLD对象(现在是一个悬空指针)。它访问v12 + 0x20处的虚函数并随后调用它,触发释放后使用漏洞。
在典型的释放后使用利用场景中,我们寻找合适的对象作为利用原语。在内核模式下,可以利用各种对象实现此目的。然而,在用户模式下(TAPISrv作为RPC服务运行),漏洞只能利用同一进程内的对象进行利用。因此,如果目标进程中不存在合适的对象,则无法直接利用漏洞。
在这种情况下,我们需要跳出框框思考。与其寻找合适的对象作为利用原语,目标是找到允许我们创建内存分配的原语,其中大小和内容都可以完全控制。通过这种方法,我们可以制作一个伪造对象来利用漏洞。
其中一个这样的原语是名为TRequestMakeCall的调度函数,它打开注册表键Software\Microsoft\Windows\CurrentVersion\Telephony\HandoffPriorities并分配内存来存储键值。
通过使用注册表编辑器检查注册表,我们可以确认此注册表键为用户配置了完全控制权限。
由于此键完全由用户控制,它允许我们操纵RequestMakeCall键的值。这使我们能够控制分配的堆大小和内存内容。
Windows Telephony Service
Windows Telephony Service是Windows RPC(远程过程调用)的一个组件,允许我们像与任何标准RPC服务一样与之交互。
要使用RPC,需要一些接口定义语言(IDL)的知识。
接口定义语言(IDL)
IDL代表接口定义语言,用于定义RPC服务器的接口。它是一种属性化编程语言,类似于C头文件。理解IDL很重要,因为它帮助您了解Telephony提供的接口,并理解我们如何与Telephony服务通信。但别担心——它相当简单。让我们一起深入探讨!
电话服务提供三个接口,可以定义如下:
|
|
ClientRequest
这里最重要的接口是ClientRequest。它允许您通信,或者换句话说,使用Telephony在gaFuncs表中提供的各种函数。
在IDA中的gaFuncs。
如您所见,ClientRequest函数将缓冲区pBuffer作为其第二个参数。pBuffer结构定义如下:
- 第一个元素(索引0处)始终是gaFuncs表中函数的偏移量。例如:
- 要调用GetUIDllName,偏移量为0x1。
- 要调用TUISPIDLLCallback,偏移量为0x2,依此类推。
- 缓冲区中的第二个元素代表调用函数的第一个参数。第三个元素对应第二个参数,依此类推。
- 缓冲区中的每个元素占用4字节,相当于整数的大小。
要调用GetUIDllName,您的伪代码将是:
|
|
利用步骤
使用上述利用原语,可以通过以下设置利用释放后使用漏洞:
- 使用GetUIDllName创建GOLD对象。
- 使用FreeDialogInstance释放GOLD对象,从而创建悬空指针。
- 操纵TRequestMakeCall的注册表键值,为释放后使用场景制作伪造对象。
- 最后,调用TUISPIDLLCallback触发对伪造对象的释放后使用,控制程序执行流。
这些步骤的可视化如下所示。
在此阶段,我们能够控制RIP(返回指令指针)指向任何所需位置。然而,控制流防护(Control Flow Guard)缓解措施阻止了在此利用尝试中创建ROP(返回导向编程)链。有关控制流防护的更多信息,请参阅以下参考文献:
- 控制流防护缓解
- 绕过控制流防护
尽管如此,仍然可以调用二进制文件导入的Win32函数。例如,我可以如下调用VirtualAlloc。
实现LoadLibraryW以加载我们控制的DLL文件的利用步骤如下:
步骤1:利用上述释放后使用漏洞调用malloc函数。如果malloc成功执行,它将返回指向分配地址的指针。在TUISPIDLLCallback中,返回值的低32位被写入RPC输入缓冲区并返回给客户端。因此,这导致有用的信息泄漏。
我们成功泄漏了低32位。
步骤2:利用上述泄漏的低32位调用VirtualAlloc,从而创建读-写-执行(RWX)内存区域。
步骤3:使用memcpy_s将DLL路径复制到先前创建的RWX内存区域。复制操作每次限制为三个字符,因此我们必须重复此步骤几次。
步骤4:调用LoadLibrary,路径指向我们的hack.dll(反向shell),该路径在步骤3中被复制到RWX内存区域。这将导致具有NT Authority\Network Service权限的shell。
步骤5:NT Authority/Network Service具有SeImpersonate权限。SeImpersonate是Windows权限,授予用户或进程模拟另一个用户或帐户的安全上下文的能力。此权限允许进程假定不同用户的身份,使其能够以该用户身份执行操作或访问资源。然而,如果未正确管理或授予未经授权的用户或进程,SeImpersonate可能构成重大安全风险。在这种情况下,我们可以使用PrintSpoofer漏洞利用来提升至NT Authority/System权限。有关PrintSpoofer漏洞利用的更多详细信息,请参见PrintSpoofer。
CVE-2024-43626 - GetPriorityList中的越界写入错误导致SetPriorityList中的信息泄漏
在对此n日漏洞进行进一步研究时,我的同事Chen Le Qi在利用过程中使用的其中一个函数中发现了一个有趣的错误。特别感谢他的宝贵帮助以及在撰写此博客文章期间的见解分享。
此错误位于tapisrv.dll中的GetPriorityList函数内,允许越界写入原语,随后在SetPriorityLeak中导致信息泄漏。它允许攻击者泄漏GOLD对象的地址。
GetPriorityList函数在早期利用步骤中用于喷洒堆。以下是其功能的更多详细信息:
- 函数调用RegQueryValueExW从注册表键检索值的长度。
- 成功后,它根据注册表键值的长度在堆上分配内存。
- 它再次调用RegQueryValueEx,获取值并使用_wcsupr将注册表键的值转换为大写,并将结果存储在*a3(第三个参数)中。
- 问题发生在最后一步,当函数调用_wcsupr将注册表键的值转换为大写时。_wcsupr函数和GetPriorityList未验证输入字符串是否以空字符结尾。因此,如果注册表键的值不以空字符结尾,_wcsupr将读取超出缓冲区直到遇到空字节(\0)。这可能导致后续的信息泄漏,因为读取的额外数据存储在*a3中并返回给调用者。
在缓冲区*a3返回后,它随后在SetPriorityList函数中使用。
如上图所示,调用lstrlenW函数计算lpString的长度,该字符串将保存到注册表键中。不幸的是,lstrlenW假定输入字符串以空字符结尾。如果字符串在GetPriorityList中损坏并随后传递给SetPriorityList,它将被保存到注册表键中。此行为可能导致信息泄漏。
有两个代码路径可用于利用此漏洞:
- ClientAttach -> ClientDetach与RequestMediaCall键。
- LSetAppPriority与RequestMakeCall键。
在最近的更新中,Microsoft通过显式空终止字符串修复了此错误,防止_wcsupr读取超出分配的缓冲区。此单一补丁还应解决GetPriorityList和SetPriorityList中的两个错误,因为是GetPriorityList接受未空终止的字符串。
此错误已被命名为CVE-2024-43626,并在2024年11月12日的更新中修复。有趣的是,这不是Microsoft在补丁中解决的唯一问题。如果我们仔细观察,可以在第20行看到另一个补丁,其中对cbData实施了整数溢出检查。cbData变量表示在GetPriorityList内首次调用RegQueryValueExW时获取的注册表键值的长度。
在检索cbData值后,它用于通过调用HeapAlloc(大小为cbData + 2)为注册表键值分配内存。如果cbData达到DWORD的最大值,这可能导致整数溢出。
然而,在测试期间我无法创建如此大的注册表键值。此限制存在是因为注册表键值的长度有限。如果任何人对创建大型注册表键值有见解,请在下方评论,我将非常感谢您的知识。提前感谢!
漏洞复现(可选)
- 使用命令
sc start Tapisrv启动Telephony服务。Telephony服务默认不运行,但您可以使用普通用户权限启动它。启动后,它将在NT Authority/Network Services权限下运行。 - 使用命令
sc query Tapisrv验证Telephony服务是否正在运行。 - 将hack.dll复制到桌面路径C:\Users\user01\Desktop\hack.dll。这是一个简单的反向shell,源代码可以在附带的概念验证中找到。
- 运行Set_Null_DACL.exe将hack.dll的DACL设置为NULL,这意味着每个人都可以访问。此程序的源代码可以在附带的