需要云计算吗? 即刻开始体验

你越界了——打扰了主人的休息

Akamai Wave Blue

寫於

Ben Barnea

December 14, 2022

Akamai Wave Blue

寫於

Ben Barnea

Ben Barnea 是 Akamai 的安全研究人员,他专注于 Windows、Linux、物联网及移动设备等各种架构方面的低级别安全研究和漏洞研究并拥有丰富的经验。他喜欢了解复杂机制的工作原理,尤其是它们是如何失效的。

MS-RPC 研究尚且存在较多不足,这可产生很多切实影响。其中一个影响便是暴露了 RPC 接口中的漏洞。
MS-RPC 研究尚且存在较多不足,这可产生很多切实影响。其中一个影响便是暴露了 RPC 接口中的漏洞。

执行摘要

  • Akamai 研究人员 Ben Barnea 在 Microsoft Windows RPC 服务中发现了两个重要漏洞,这些漏洞被指定为 CVE-2022-37998CVE-2022-37973 ,基本评分为 7.7。
  • 这两个漏洞主要利用本地会话管理器 RPC 接口中的一些 bug。
  • 这些漏洞可引发拒绝服务攻击,从而造成容器和会话服务(例如 Microsoft Defender 应用程序防护、沙盒、Docker 和 Windows 终端服务器)无法正常工作。
  • 未安装补丁的 Windows 10、Windows 11 和 Windows Server 2022 计算机中存在该漏洞。
  • 漏洞已披露给 Microsoft,并在 2022 年 10 月 Patch Tuesday 中得到了解决。
  • 我们在研究存储库中 提供了这一概念验证

前言

在过去一年中,Akamai 安全情报组对 MS-RPC 开展了深入探究。MS-RPC 是一种功能繁多的协议,但其研究尚且存在较多不足,这可产生许多切实影响。其中一个影响便是暴露了 RPC 接口中的 漏洞。这便是本篇博客文章重点介绍的内容:本地会话管理器 (LSM) RPC 接口中的漏洞。 

LSM 是会话管理器子系统中的一项服务。LSM 负责管理与 Windows 计算机上的终端服务器会话相关的本地会话。它能与 Winlogon、Csrss 及其他 Windows 相关组件进行通信。 

LSM 以 lsm.dll 形式实现, 并且包含客户端和服务器逻辑。LSM 公开了若干 RPC 接口,其中一个较为值得关注的接口与 Hyper-V 虚拟机中运行的容器会话管理相关。这两个漏洞便位于该接口内。

这个接口是什么?

新 RPC 接口的指定 UUID 为 c938b419-5092-4385-8360-7cdc9625976a。该接口恰好公开了两个函数: ContainerCom_AskForSessionContainerCom_SessionLoggedOff。此外,该接口注册了安全回调,该回调始终返回 RPC_S_OK,从而允许所有人访问。LSM 服务器注册了一个 Hyper-V 套接字 (hvsocket) 端点,仅可通过 Hyper-V 容器进行访问。

图 1:在客户端通过 hvsocket 建立 RPC 连接与在服务器端建立 hvsocket 端点

图 1:在客户端通过 hvsocket 建立 RPC 连接与在服务器端建立 hvsocket 端点

在容器内创建会话之后(例如,因 RDP 连接而创建),LSM 客户端会首先调用 容器 LSM 中的 RpcGetRequestForWinlogon。该函数负责仲裁会话创建,它在容器内运行时,首先会向主机请求权限。为此,它会使用父项的一个 hvsocket,执行针对主机的 ContainerCom_AskForSession RPC 调用。RPC 接口对连接到容器的会话数设有限制。为实施这一限制,它会跟踪新创建的会话。

LSM 如何跟踪会话?

答案很简单。有一个名为 ContainerSessionServer 的全局对象,其包含两个用于跟踪会话的变量:

  1. 统计已创建会话总数的计数器。 其值仅限为 1,也就是说,在任意给定时刻,仅允许创建一个会话。
  2. 容器的 GUID 与其容器会话计数之间的映射。 对于每个容器,其值仅限为 2。

在一个容器每次请求会话时, ContainerSessionServer::AskForSession 首先都会检查会话总数计数器值是否小于 1。如果小于 1,则增加会话总数计数,也会增加映射中的容器会话计数器值。

当调用 ContainerSessionServer::OnSessionLoggedOff 时(在容器退出时调用,或者直接作为 RPC 调用),该函数将会话总数计数以及容器会话计数均减 1。 

详细审视 RPC 函数中的漏洞

虽然该接口听起来非常简单,实现也很简单,但我们发现了四个 bug,并将这些 bug 串联为两个漏洞。

串联 #1:利用关键部分发起 DoS——CVE-2022-37998

Bug #1——无法退出关键部分

ContainerSessionServer::AskForSession 利用一个关键部分来同步对全局对象 ContainerSessionServer的访问。 

图 2:漏洞的反编译代码。该函数未释放关键部分便退出

图 2:漏洞的反编译代码。该函数未释放关键部分便退出

如上图所示,从第 112 行开始进入该关键部分。随后,在第 114-116 行中,它会检查容器会话计数器是否达到了限值 (2)。如果已达限值,LSM 将不再跟踪该会话,并会立即退出函数(第 125 行)。遗憾的是,代码并未退出先前进入的关键部分。因此,再次调用该接口时系统会卡住,一直在等待该关键部分释放。

但不要忘记,会话总数计数器的限值是 1,那怎样才能让容器的会话计数器值达到 2 呢?从逻辑上来说,这根本不可能实现。不过,这里还有第二个 bug!

Bug #2——计数器跟踪错误

注销与容器的会话后,将对主机中的 ContainerSessionServer::OnSessionLoggedOff 进行 PRC 调用。该函数首先通过调用 DecreaseTotalSessionCount 减少会话总数计数器的值。无论是否在跟踪容器,该函数都会执行此操作。如果该函数发现并未跟踪容器,则会退出,但不会把之前减去的会话总数计数器值加回来。

这会导致会话总数计数器值为负(因为该计数器的值类型是有符号整数)。我们只需先发送多个 OnSessionLoggedOff 请求,然后再发送任何 AskForSession 请求,从而不断将会话总数计数器值减少至任意负值。

第一个和第二个 bug 的串联

我们可使用 bug #2 将会话总数计数器值减少数次,直至其变为负值。然后通过向 AskForSession发送两个请求来利用 bug #1。第二次调用该函数时,函数会检查会话总数计数器的值是否小于 1——由于第二个 bug,这个值确实小于 1。随后在该函数检测到容器会话计数器值为 2 时,它就会返回,但不会退出关键部分。

图 3:漏洞利用过程概况图

图 3:漏洞利用过程概况图

DoS 取决于新传入的 RPC 调用是否会分配至创建关键部分死锁的同一线程。如果 RPC 运行时将新调用分配至同一线程,则不会发生 DoS,因为 EnterCriticalSection 支持嵌套所有权,即同一线程可调用 EnterCriticalSection 两次。如果 RPC 调用被分配至其他任何线程,而非造成关键部分死锁的那个线程,则该调用将永远处于等待状态。

串联 #2:利用内存泄漏发起 DoS——CVE-2022-37973

Bug #3——内存泄漏

ContainerSessionServer::AskForSession 还会跟踪容器的事件,例如容器退出/暂停/恢复等。其实现方法是使用容器的 GUID 调用 HcsOpenComputeSystem ,然后使用 HcsRegisterComputeSystemCallback来注册回调。 

已注册的回调接收上下文对象。上下文在 ContainerSessionServer::AskForSession内分配。但如果发生错误,许多情况下函数退出时不会释放为上下文分配的内存。这就会造成内存泄漏,攻击者可多次触发该内存泄漏。在调用数量达到一定程度后,LSM 进程将由于内存耗尽而崩溃。 

我们在测试中发现,通过无限循环发送 RPC 请求时,每秒可分配 3 MB 的内存。在本测试中,在分配的内存量达到 24 GB 后,LSM 服务便崩溃了。耗尽 24 GB 的内存大约需要两小时。该服务不会自动重新生成。

Bug #4——远程访问

MS-RPC 中的端点是多路复用的。如果服务器注册了多个接口和多个端点,则可通过每个端点访问每个接口。端点和接口之间 未相互绑定。

该接口本应只能通过容器的 hvsocket 访问。但在本测试中,LSM 注册了可远程访问的命名管道端点“\pipe\LSM_API_service”。由于端点是多路复用的,远程攻击者可连接命名管道端点,然后向容器接口发送请求。修复方法很简单——安全回调应检查客户端使用的是哪个端点,如果不是 hvsocket,则拒绝访问。

第三个和第四个 bug 的串联

容器跟踪功能基于客户端标识符属性。也就是说,对于 hvsocket 而言,其客户端标识符应该是容器的 GUID。对于命名管道,其客户端标识符应该是客户端的机器名称。

要触发第一项漏洞利用攻击,客户端必须拥有客户端标识符,该标识符是正在运行的容器的实际 GUID。因此,远程客户端无法触发这些 bug,除非攻击者能成功查明正在运行的容器的 GUID,并更改其机器名称,但这基本行不通。

可惜由于第三个 bug(内存泄漏)的存在,系统会直接分配对象,而不会对所请求的容器执行任何检查。这意味着远程攻击者(利用 bug #4)可远程触发内存泄漏。通过多次调用,攻击者就能造成内存耗尽,进而导致进程崩溃。

影响

虽然这些漏洞被归为拒绝服务 (DoS) 类漏洞,但攻击者可利用它们绕过安全功能,因此它们确实会造成安全性方面的影响。

利用第一个漏洞(关键部分),攻击者可对特定新接口发起 DoS 攻击。该问题会阻止创建新的沙盒实例。

利用第二个漏洞发起的攻击可从远程或容器内触发,并导致整个进程崩溃。这会造成所有依赖 LSM 的功能失效,Microsoft Defender 应用程序防护和沙盒等安全功能也不例外。此外,RDP 和 Docker 将无法正常运行。

图 4:Microsoft Edge 中显示的 MDAG 错误

图 4:Microsoft Edge 中显示的 MDAG 错误

总结

这些漏洞充分体现了一点:看似简单或微不足道的事物能产生重大的负面影响。这个接口一点都不起眼:但它存在一些很容易触发并且能产生重大影响的 bug。 

在现实环境中,有很多恶意攻击者正在利用这些攻击链。越是不起眼的事物越容易被忽视,从而给漏洞利用者创造了理想的下手机会。 

结合我们长期以来在 RPC 领域的相关经验,我们鼓励其他研究人员探索其他 RPC 接口中的类似 bug。

如果您对此类 RPC 研究话题比较感兴趣,欢迎查看我们的 RPC 工具包 ,其中提供了更多详细记录和工具。您还可以关注我们的 公众号 ,实时获得关于此项研究和 Akamai 其他研究的新鲜资讯。



Akamai Wave Blue

寫於

Ben Barnea

December 14, 2022

Akamai Wave Blue

寫於

Ben Barnea

Ben Barnea 是 Akamai 的安全研究人员,他专注于 Windows、Linux、物联网及移动设备等各种架构方面的低级别安全研究和漏洞研究并拥有丰富的经验。他喜欢了解复杂机制的工作原理,尤其是它们是如何失效的。