COMpromise - 利用 TOCTOU 竞争获取 SYSTEM 权限

admin 2025年2月10日11:19:58评论8 views字数 6161阅读20分32秒阅读模式

【翻译】The Key to COMpromise - Abusing a TOCTOU race to gain SYSTEM, Part 2 — Neodyme 

回顾

在本系列博客的第一篇文章中,我们探讨了各种安全产品的架构设计,并演示了如何利用 COM 劫持来攻击它们:我们研究了一个允许我们通过命名管道重放修改后消息的漏洞,突出了一个潜在的攻击向量。

如前所述,许多安全产品都有在非特权用户上下文中运行的前端进程,这些进程能够通过与具有更高权限的后端服务交互来启动特权操作 — 比如添加排除项。为了防止滥用,大多数供应商都实施了机制来确保这些操作来自受信任的进程,并采取措施防止这些进程被篡改。

然而,由于前端进程是以有限的用户权限执行的,COM 劫持提供了一个将恶意 DLL 加载到进程中的机会。在我们的研究中,我们发现这种攻击向量在我们针对的所有产品中都是可行的,使我们能够利用安全产品对其自身进程的固有信任。

为了利用这种信任关系,我们需要对前端和后端进程之间的通信协议进行逆向工程。这帮助我们识别可以被操纵以提升权限的交互。

在这篇文章中,我们将深入探讨如何利用 AVG Internet Security (CVE-2024-6510) 中的这种信任来获取提升的权限。但在此之前,下一节将详细介绍我们如何克服一个最初扰乱我们 COM 劫持尝试的白名单机制。

COMpromise - 利用 TOCTOU 竞争获取 SYSTEM 权限
AVG Internet Security 解决方案的用户界面

绕过白名单

在我们研究的这一部分,我们采用了与之前相同的基本技术,但有一个关键的区别:这次,每当我们打开文件对话框来阻止应用程序时,COM 接口就会被触发。然而,我们遇到了一个限制 — 我们不能从任意文件夹加载我们的 DLL。

当尝试从我们自定义的 C:poc 文件夹加载 DLL 时,我们在 Process Monitor 中观察不到任何成功的 DLL 加载。相比之下,原始的 DLL 路径可以正常工作。通过反复试验,我们发现将我们的 DLL 放在 C:Windowssystem32 目录下可以成功加载。这种行为表明该产品会根据白名单验证 DLL 的目录,这可能是防御 DLL 劫持攻击的一种措施。

虽然从 C:Windowssystem32 加载可以绕过白名单,但这种方法对于我们的权限提升来说并不实用,因为非特权用户无法写入该目录。然而,基于我们之前绕过 AppLocker 配置的经验,我们知道 C:Windowssystem32 中的某些子目录是非特权用户可写的。其中一个这样的目录是 C:WindowsSystem32spooldriverscolor。通过将用于 COM 劫持的 DLL 放在这个可写的子目录中,我们成功绕过了白名单并在前端进程中实现了代码执行。

COMpromise - 利用 TOCTOU 竞争获取 SYSTEM 权限
SYSTEM32 中特定文件夹的示意性 ACL

建立了这种 DLL 注入方法后,下一步就是分析与后端进程的通信。在下一节中,我们将讨论如何利用这个基本功能来操纵信任关系并提升权限。

逆向工程 RPC 通信

逆向工程 RPC 通信可能是一项艰巨的任务,特别是在开始时。幸运的是,像 RpcView 这样的工具对于枚举和识别 RPC 接口非常有价值。然而,这个过程最终需要深入的逆向工程工作。对于我们的 AVG 工作,我们使用了优秀的 Akamai Research RPC Toolkit 来识别和分析各个 AVG 二进制文件中的各种 RPC 接口。

我们的重点是 RPC server 接口,因为这些是高权限进程暴露的端点。虽然 AVGSvc.exe 可执行文件不包含 RPC 服务器绑定,但我们发现服务加载的 ashServ.dll DLL 确实暴露了这样的接口!

Akamai RPC Toolkit 产生了以下输出:

"ashServ.dll": {// [...]"908d4c23-138f-4ac5-af4a-08584ae7c67b": {"number_of_functions"22,"functions_pointers": ["0x1654e0700","0x1654e0790",// [...]],"role""server","flags""0x6000000","interface_address""0x165f96020",// [...]"eb915940-6276-11d2-b8e7-006097c59f07": {"number_of_functions"106,"functions_pointers": ["0x1655c8180","0x1655c8290",// [...]"role""server","flags""0x6000000","interface_address""0x165fca670"       },"1118fbbd-02ee-4910-9d86-9940537ee146": {"number_of_functions"23,"functions_pointers": ["0x1655c08d0","0x1655c6be0",// [...]],"role""server","flags""0x6000000","interface_address""0x165fccfb0"}

从这个输出中,我们可以观察到三个主要接口,分别有 22 个、106 个和 23 个暴露的端点。其中最大的接口是 [Aavm] RPC 接口,这个接口已经成为了之前研究和利用的主题。在网上搜索接口 GUID 还能发现一些2015 年的有趣博客文章

对 RPC 接口中的函数进行逆向工程和重命名是一项繁琐但相对直接的工作。

COMpromise - 利用 TOCTOU 竞争获取 SYSTEM 权限
Aavm RPC 接口中一些重命名的 RPC 函数

通过这项分析,我们发现了一个名为 AavmRpcRunSystemComponent 的 RPC 函数,它在没有 RPC 模拟的情况下使用 CreateProcess API:

.rdata:0000000165FCA550                 dq offset sub_1655C5580.rdata:0000000165FCA558                 dq offset sub_1655C55D0.rdata:0000000165FCA560                 dq offset AavmRpcRunSystemComponent.rdata:0000000165FCA568                 dq offset DecryptData.rdata:0000000165FCA570                 dq offset AddNetAlert

当 RPC 客户端未被模拟时,通过这个函数创建的任何新进程都将以 SYSTEM 权限运行,这为权限提升创造了一个关键机会。然而,在这个进程启动之前,会进行一次 DSA_FileVerify 检查:

__int64 __fastcall AavmRpcRunSystemComponent(__int64 a1, unsignedint whitelist_id, __int64 arguments, DWORD *out_pid){// [...]char out_string[32];// [...] v8 = GetFileById(out_string, whitelist_id); // [1]// [...] FileW = CreateFileW((LPCWSTR)out_string, 0x800000001u0i64, 3u0x8000000u, 0i64); v12 = FileW; v21 = (__int64)FileW;if ( FileW == (HANDLE)-1i64 ) {// file not found }if ( !GetFinalPathNameByHandleW(FileW, szFilePath, 0x104u, 0) ) {// File path could not be resolved }if ( whitelist_id != 2 && !(unsigned __int8)DSA_FileVerify(szFilePath, 0i64, 18i64) ) // [2] { LastError = 87// ERROR_INVALID_PARAMETER    CloseHandle(v12);return LastError; }// [...]snprintf(combined_arguments, v15, L"%s %s", szFilePath, arguments); // [3]// [...]if ( CreateProcessW(szFilePath, combined_arguments, 0i64, 0i64, 000i64, 0i64, &StartupInfo, &ProcessInformation) ) // [4] {// Win ?

DSA_FileVerify 函数执行以下几项验证:

  1. 基于 [1] 中的整数参数,它返回一个文件名。这个列表中的大多数可执行文件都是修复或安装工具,如 aswOfferTool.exeSupportTool.exe 和 AvEmUpdate.exe,这将选项限制在这些预定义的二进制文件中。
  2. 在 [2] 中执行文件签名验证,以确保只有受信任的二进制文件可以被执行。这个检查防止攻击者插入他们自己的恶意二进制文件到进程中。
  3. 最后,在 [3] 中构造程序参数,并在 [4] 中以 SYSTEM 权限创建进程。

虽然这个函数看起来是一个很有前途的权限提升向量,但允许列表中的二进制文件和文件签名验证的限制构成了重大障碍。如果无法利用任何允许列表中的程序,这条路径可能看起来就是死胡同。

为了克服这个限制,我们决定尝试使用在 aavmrpch.dll 库中找到的 RPC 客户端绑定。使用这种方法,我们开始测试各种 RPC 接口的功能,特别关注 AavmRpcRunSystemComponent 函数,以探索潜在的利用路径。

滥用更新机制

最有希望的攻击目标是 AvEmUpdate.exe 可执行文件,它接受多种命令行参数。这个可执行文件负责安装以 cab 或 DLL 文件形式提供的更新。由于我们可以控制传递给它的参数,这为进一步探索提供了一个引人注目的机会。

一个特别有趣的参数是 /applydll,它允许进程加载指定的 DLL。至关重要的是,由于该进程以 SYSTEM 权限运行,这个参数可能被滥用来提升权限。然而,更新机制包含一个额外的安全措施:它验证提供的 DLL 是否由制造商签名。这个签名检查阻止我们直接提供自定义 DLL 来获取 SYSTEM 权限。

TOCTOU 竞争

尽管有这个限制,我们确信通过仔细分析和利用进程的逻辑,我们可以绕过完整性检查。我们最终在逻辑中发现了一个使用时间与检查时间(TOCTOU)问题,这使得完整性检查可以被绕过。为了可靠地利用这一点,我们采用了 OpLocks(机会锁)和 junction(联接)的组合。

为了在利用过程中控制文件访问的时机并可靠地利用我们的竞争条件,我们需要一种方法来使更新进程处于等待状态。在这里,我们使用 OpLock 来阻止对 DLL 文件的访问,并强制更新进程等待我们释放 OpLock。即使在以非特权用户身份操作时,这也适用于以 SYSTEM 身份运行的进程。这给了我们时间来准备下一步。

我们还希望能够在持有 OpLock 的同时切换 DLL 文件。这就是 junction 发挥作用的地方。Junction 是可以将文件系统访问重定向到不同位置的符号链接。由于非特权用户可以创建 junction,我们在持有 OpLock 时使用这个功能来重定向文件访问。我们可以将 junction 指向另一个位置以进行下一次文件访问,并精确控制每次单独的文件访问所访问的文件。有关 OpLocks 和 junction 的更多信息,请参考 James Forshaw 提供的代码和 ZDI 的这篇文章

以下是漏洞利用的工作原理:

  1. AvEmUpdate.exe 进程在加载 DLL 之前进行多次文件访问,可能是为了验证其合法性。
  2. 使用 junction,我们将进程重定向到一个有效的、已签名的 DLL 进行前三次文件访问尝试。
  3. 在第四次文件访问时,当进程试图加载 DLL 时,我们将 junction 重定向到包含权限提升 payload 的恶意 DLL。

因为我们在前三次文件访问时持有 OpLock,所以我们可以在 SYSTEM 进程等待访问前一个文件时动态更改 junction 的目标。更新 junction 的目标后,我们释放 OpLock,允许进程继续进行下一个文件。我们重复这个过程,直到第四次访问成功加载我们的恶意 DLL。

COMpromise - 利用 TOCTOU 竞争获取 SYSTEM 权限
Junction 重定向的可视化

注意,进程始终访问相同的文件;然而,使用 junction,我们改变了在这个路径下可访问的文件。

虽然这种技术成功地让我们绕过了签名验证并加载了恶意 DLL,但仅凭这一点还不足以完全提升权限。在下一节中,我们将深入探讨在系统上获取高权限所需的额外步骤以及我们遇到的挑战。

禁用自我防护

即使在成功执行 TOCTOU(使用时间与检查时间)竞争之后,我们的恶意 DLL 也没有被加载到进程中。经过进一步调查,我们发现该进程只加载具有有效签名的 DLL。这一额外的保护层显著增加了我们利用的难度。我们怀疑这种行为是由于进程作为 PPL(Protected Process Light)进程启动造成的。

经过一些尝试和错误,我们发现这个限制只在产品的自我保护功能启用时才会执行。幸运的是,我们发现了一个 RPC 函数 AavmRpcDisableSelfDefense,可以禁用这个自我保护机制。这个函数由我们之前在 RPC 调用中已经交互过的相同 DLL(ashServ.dll)导出。通过调用这个函数,我们成功禁用了产品的自我防护功能。

禁用自我防护后,我们的恶意 DLL 成功加载到以 SYSTEM 权限运行的进程中,最终完成了权限提升。

总结一下,这个案例中的利用过程如下:

  1. 使用 COM 劫持的初始入口:
    • 我们使用 COM 劫持将 DLL 加载到前端进程中。
    • 为了绕过允许列表机制,DLL 被放置在 C:WindowsSystem32spooldriverscolor 中。
  2. 禁用自我防护:
    • 加载的 DLL 然后调用 AavmRpcDisableSelfDefense 函数来停用产品的自我保护功能。
  3. 触发更新机制:
    • DLL 通过调用 AavmRpcRunSystemComponent 触发更新。
    • 使用 junction 结合 OpLocks,我们欺骗更新进程加载未签名的 DLL。
    • 这使我们能够将权限提升到 SYSTEM

总结

在这篇博文中,我们演示了如何利用 COM 劫持来获取 AVG Internet Security 的 SYSTEM 权限以提升权限。与之前的案例不同,我们遇到了额外的障碍,即最初阻止我们的 DLL 的允许列表机制。我们描述了如何通过将 DLL 放置在可写的系统目录中来绕过这个限制。我们详细说明了对产品 RPC 调用的逆向工程,这揭示了允许我们禁用自我保护和触发更新机制的函数。通过结合 junction 和 OpLocks,我们绕过了签名检查并成功加载了未签名的 DLL,使我们能够将权限提升到 SYSTEM

原文始发于微信公众号(securitainment):COMpromise - 利用 TOCTOU 竞争获取 SYSTEM 权限

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月10日11:19:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   COMpromise - 利用 TOCTOU 竞争获取 SYSTEM 权限https://cn-sec.com/archives/3720392.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息