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

远程关闭漏洞——使用 Wininit.exe 的远程 DoS 攻击

Stiv Kupchik

寫於

Stiv Kupchik

January 31, 2023

Stiv Kupchik

寫於

Stiv Kupchik

Stiv Kupchik 是 Akamai 安全研究员团队的负责人。他的研究项目主要涉及操作系统内核、漏洞研究和恶意软件分析等领域。他曾在 Black Hat、Hexacon 和 44CON 等会议上展示过他的研究成果。除了是一名网络安全专业人士外,Stiv 还拥有物理学学士学位。

Wininit 是一种 Windows 关键进程,在 Windows 操作系统的启动和关闭中发挥着关键作用(关键到一旦它崩溃,整个系统都会崩溃)。

编辑和内容补充:Tricia Howard

执行摘要

  • Akamai 研究人员 Stiv Kupchik 在 Microsoft 的 Wininit.exe 中发现了一个新的拒绝服务 (DoS) 漏洞,其 CVE 编号为 CVE-2022-44707,CVSS 评分为 6.5。

  • 本着负责任的态度,我们已经在 8 月将此漏洞披露给 MSRC,他们随后在 2022 年 12 月的 Patch Tuesday中修复了此漏洞。

  • 此漏洞滥用 RPC 缓存机制,我们就此开展了大量研究。在 RPC 工具包中,我们提供了攻击概念证明。

  • 利用此漏洞,攻击者可以借助缓存机制绕过安全检查,并与远程 Windows 设备上的关闭机制交互,从而获得停止或发起关闭的控制权。

  • 此漏洞会影响 Windows 8/Server 2012 及以上的、所有未安装补丁的 Windows 版本,所以请务必及时安装补丁。

简介

在先前名为 《冷硬缓存》的博文中,我们曾预告说将会为大家介绍使用我们的 RPC 工具包发现的更多 RPC 漏洞,这篇博文就是为此而撰写。在今天的这一期“RPC 诡计”中,我们的主角是 Wininit。  

Wininit 是一种 Windows 关键进程,在 Windows 操作系统的启动和关闭中发挥着关键作用(关键到一旦它崩溃,整个系统都会崩溃)。因此它公开了多个用来实现关闭功能的 RPC 接口,部分接口 甚至有文档记录。我们发现的漏洞存在于 WindowsShutdown 接口中。

WindowsShutdown 接口是什么?

WindowsShutdown 是 Wininit 公开的数个 RPC 接口之一。它负责关闭进程——作用与 Wininit 的其他 PRC 接口一样,是不是有点意外?该接口的 UUID 是 d95afe70-a6d5-4259-822e-2c84da1ddb0d。它归入 MS-RSP (远程关闭协议),具有如下功能: 

WindowsShutdown 公开的函数:WsdrInitiateShutdown、WsdrAbortShutdown 和 WsdrCheckForHiberboot 图 1:WindowsShutdown 接口的功能

从公开的 IDL 文件中,我们可以看到 只有 WsdrInitiateShutdown 和 WsdrAbortShutdown 有文档记录。我们是否应该关注没有文档记录的函数(从没有人问过研究人员这个问题)?为了回答这个问题,我们先来看一下这个接口的安全回调。

安全回调

回调首先会检查传输协议,并且仅允许 ALPC 或 TCP 连接。随后它会检查身份验证级别,仅允许 RPC_C_AUTHN_LEVEL_PKT_PRIVACY 。如果所调用的函数不是 WsdrCheckForHiberboot,系统会检查远程调用方用户的令牌。为此,系统会将该令牌与 SECURITY_NETWORK_RID这个广为人知的 SID(在 WinInit 初始化过程中创建,保存在一个全局变量中)进行比较。安全回调完全不会限制对 WsdrCheckForHiberboot 的调用。

WindowsShutdown 的安全回调函数反编译 图 2:针对 WindowsShutdown 的安全回调

该 RCP 接口已注册默认缓存行为,所以理论上来说,如果我们能成功调用 WsdrCheckForHiberboot,就能借助缓存的成功结果,在后续对 WsdrInitiateShutdownWsdrAbortShutdown进行调用时绕过 SID 检查。

那么我们需要做些什么来调用 WsdrCheckForHiberboot

WsdrCheckForHiberboot

我们并不关心这个函数的功能究竟是什么,也不关心 hiberboot 是什么意思(不过为了满足大家的好奇心,这是 Windows 的一种快速启动机制),但我们需要了解它的正确调用方法。我们要在 IDL 文件中正确定义该函数,以便为其编译客户端。虽然这个函数并没有相应的文档记录,我们还是可以观察一下 advapi CheckForHiberboot,这是我们找到的 WsdrCheckForHiberbootRPC 客户端中,唯一配有文档记录的一个。它只需要两个参数:一个布尔指针,一个布尔值。

CheckForHiberboot 的 advapi32 反汇编 图 3:advapi CheckForHiberboot——我们找到的 RPC 客户端中,唯一配有文档记录的客户端

我们可以使用这些参数复现该函数定义,并编译一个 IDL 文件和程序。但这里还缺少一点东西。在使用我们的客户端远程调用此函数时,RPC 运行时返回了一个 PRC 存根错误,这让我们知道,对函数的调用不正确。

根据 advapi 函数初始复现 WsdrCheckForHiberboot 定义 图 4:第一次复现 WsdrCheckForHiberboot 定义

好在我们不用盲目猜测,还有一种更合适的方法,只是要麻烦一些。在 RPC 接口存根中,有一个名为 ProcFormatString的字段。它大体上就是一个很长的二进制字符串,描述了该接口的所有函数公开的参数类型及返回值。在封送和取消封送函数调用(也就是在我们第一次尝试时,返回了 RPC 存根错误的进程)时,RPC 运行时会用到这个字段。在花了一番功夫人工解析这个二进制字符串之后,我们最终发现,该函数还要求提供另外一个参数,这个参数的类型是 wchar_t*。下面这张图片展示了 WsdrCheckForHiberbootProcFormatString中的代码段,我们针对其中每个部分添加了自己的注释:

根据 advapi 函数初始复现 WsdrCheckForHiberboot 定义 图 5: ProcFormatString 中的 WsdrCheckForHiberboot 代码段

在函数定义中添加这个新发现的参数之后,我们终于成功了! CheckForHiberboot 成功返回了结果,可以继续调用 WsdrInitiateShutdownWsdrAbortShutdown了。

再补充几句……

我们最想立刻实现的就是使用缓存攻击调用 WsdrInitiateShutdown 并实现远程关闭。这次没有涉及到任何猜测。我们不但在 IDL 文件中找到了函数定义的记录,还在 advapi 函数 InitiateShutdownA下找到了该函数预期获得的标记的记录。利用 SHUTDOWN_GRACE_OVERRIDE、SHUTDOWN_HYBRID 和 SHUTDOWN_FORCE_OTHERS 标记组合,我们就能实现立即强制关闭。

这样就完成了攻击链,我们绕过了安全回调及其 SID 检查,还实现了远程关闭。从技术上来说,这属于特权升级(允许任意通过身份验证的用户远程调用 WsdrInitiateShutdown,而非仅限 SECURITY_NETWORK_RID下允许的用户调用)。我们只能使用此接口实现关闭,所以这个漏洞分类为 DoS 漏洞。

检测

我们提供了 OSQuery,用于检测未安装补丁(因此受此漏洞影响)的 Wininit.exe 版本。 Akamai Guardicore Segmentation 客户可以将 Insight 功能与此查询结合使用来搜索易受攻击的资产

  WITH product_version AS (
  WITH os_minor AS (
    WITH os_major AS (
      SELECT substr(product_version, 0, instr(product_version, ".")) as os_major, substr(product_version, instr(product_version, ".")+1) as no_os_major_substr
      FROM file
      WHERE path = "c:\windows\system32\wininit.exe"
    )
    SELECT substr(no_os_major_substr, instr(no_os_major_substr, ".")+1) as no_os_minor_substr, substr(no_os_major_substr, 0, instr(no_os_major_substr, ".")) as os_minor, os_major
    FROM os_major
  )
  SELECT
    CAST(substr(no_os_minor_substr, instr(no_os_minor_substr, ".")+1) AS INTEGER) AS product_minor,
    CAST(substr(no_os_minor_substr, 0, instr(no_os_minor_substr, ".")) AS INTEGER) AS product_major,
    CAST(os_minor AS INTEGER) AS os_minor,
    CAST(os_major AS INTEGER) AS os_major
  FROM os_minor
)
SELECT
  CASE
    WHEN NOT ((os_major = 6 AND os_minor = 3) OR (os_major = 6 AND os_minor = 2) OR (os_major = 10 AND os_minor = 0))
    THEN "not supported"
    WHEN os_major = 6 AND os_minor = 3 AND product_major = 9600 AND product_minor >= 20716 THEN "patched"
    WHEN os_major = 6 AND os_minor = 2 AND product_major = 9200 AND product_minor >= 24011 THEN "patched"
    WHEN (
        (product_major = 14393 AND product_minor >= 5582)
        OR
        (product_major = 10240 AND product_minor >= 19624)
        OR
        (product_major = 19041 AND product_minor >= 1620)
        OR
        (product_major = 22621 AND product_minor >= 963)
        OR
        (product_major = 22000 AND product_minor >= 1335)
        OR
        (product_major = 20348 AND product_minor >= 1366)
        OR
        (product_major = 17763 AND product_minor >= 3770)
    )
    THEN
      "patched"
    ELSE
      "not patched"

总结

虽然这个漏洞不那么关键(它 只是 远程关闭,甚至不是未经授权的远程关闭),但体现出了 MS-PRC 固有的破坏潜力,因为 Windows 操作系统的大多数关键服务都内置这一接口。此外,不同于其他类型的漏洞,RPC 几乎不需要花心思去猜测——所有信息都摆在明面上(二进制文件中),您要做的只是找到解读方法。

我们坚持广泛研究 MS-RPC的初衷就是探寻利用这些关键函数漏洞的可能性。虽然这个接口的使用广泛,但从整体来看,相关研究相当不充分。类似于本文涉及到的这种漏洞展示了此类工作的必要性,我们希望将来看到其他研究人员就 MS-RPC 开展研究。 

本着负责任的态度,我们已经在 8 月末将此漏洞披露给 MSRC,他们随后在 2022 年 12 月的 Patch Tuesday 中修复了此漏洞。



Stiv Kupchik

寫於

Stiv Kupchik

January 31, 2023

Stiv Kupchik

寫於

Stiv Kupchik

Stiv Kupchik 是 Akamai 安全研究员团队的负责人。他的研究项目主要涉及操作系统内核、漏洞研究和恶意软件分析等领域。他曾在 Black Hat、Hexacon 和 44CON 等会议上展示过他的研究成果。除了是一名网络安全专业人士外,Stiv 还拥有物理学学士学位。