Windows WDM 驱动漏洞挖掘(下) - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com

Windows WDM 驱动漏洞挖掘(下)

luochicun 漏洞 2022-06-29 11:55:00
收藏

导语:本文,我们介绍了如何开始寻找WDM驱动程序中的漏洞。最重要的事情就是获得设备句柄。

接上篇Windows WDM 驱动漏洞挖掘(上)

蓝屏现象

简而言之,我们第一个真正的漏洞示例是微软Azure VFP扩展或vfpext.sys。故事从驱动程序的ioctl调度函数开始。在一些变量初始化之后,它调用一个方法来验证用户模式参数,然后调用一些内部逻辑。

12.png

图12-由Ida Pro反编译的SxStartDeviceIoControl函数,从vfpext.sys的disptachIoctl函数调用

我们真正拥有的是一个简单的代码,它执行以下操作:

从宏iogetcurrenttirpstacklocation (IRP)中获取一个指向CurrentStackLocation的指针;

验证缓冲区长度是否超过0x218;

以安全的方式创建一些Unicode字符串;

通过调用我们不关心的SxFindContextByName来进行查找操作;

问题是驱动程序在任何地方都不会检查给定的 ioctl IRP->AssociatedIrp 是什么。例如,SystemBuffer 是一个有效地址;它假定缓冲区的地址是有效的。

然而,回顾图 4,我们可以假设驱动程序期望获得一个带有TransferType的ioctl代码为METHOD_BUFFER或METHOD_IN_DIRECT或METHOD_OUT_DIRECT,因为它从IRP- bb0 AssociatedIrp.SystemBuffer读取输入缓冲区。

但是,如果它是 METHOD_NEITHER,那么 IRP->AssociatedIrp.SystemBuffer 对 IoManager 没有任何意义,它会将其设置为零,因为它会填充 IRP->Parameters.DeviceIoControl.Type3InputBuffer。

接下来,调用 SxInitUnicodeStringSafe ,我认为它应该是内核方法 RtlInitUnicodeString 的更安全版本;第二个参数是 SystemBuffer+8,即源字符串。

13.png

图13-Ida Pro 反编译的 SxInitUnicodeStringSafe 函数,接收 SystemBuffer,它可以指向无效内存

因此,SystemBuffer地址的地址将是0x10 = (0x0+sizeof(wchar_t *)+8)。因此,在调用RtlStringCbLengthW(顾名思义,它计算长度)时,将使用指向无效地址的系统缓冲区。它包含如下代码:

14.png

图14-这里发生了指针解引用

当 *SystemBuffer 解引用发生时,你解引用了一个无效地址,并且内核不会执行空指针。

15.webp.jpg

图15-只是由漏洞触发的蓝屏

这个漏洞检查只需要两行代码就可被触发:

16.png

图16-导致漏洞检查

在这种情况下,我们的漏洞是不可利用的,除非来自有限用户的 DoS。此漏洞也可能由受限用户和应用程序沙箱中触发,因为对设备没有任何保护。因此,每个人都可以得到一个句柄和BSoD。直到今天,这个漏洞还没有被修复; MSRC 的回应是:“我们已经完成了调查,并确定此报告是一个中等严重性的拒绝服务漏洞,很遗憾,这意味着它不符合我们在安全更新中提供服务的标准。”微软的回答很有趣,因为它不仅仅是一个常规的 DoS,而是一个系统的 DoS。从 CVSS 的角度来看,它应该是 7.3。除此之外,修复非常简单;在盲目使用之前检查ioctl代码,这将导致正确使用IRP的缓冲区。

内存漏洞

在了解了拒绝服务攻击后,我们应该继续讨论一个特殊的、隐秘的漏洞类。信息泄漏是指内核内存泄露给用户模式。它不会触发漏洞检查或任何异常,因此在驱动程序开发的常规管道中很难找到。与常规内存池漏洞相比,你也无法通过驱动程序验证程序找到它。

自Windows Vista发布以来,微软引入了KASLR缓解措施,这使得 Windows 内核开发非常具有挑战性。 KASLR是内核ASLR,它在启动时随机化驱动程序、模块、内核对象的基地址。这样,你就不能像过去那样滥用任意写入原语了。那时,你可以找到一个可以被内核调用的地址,比如HalDispatchTable,然后放置一个恶意的shellcode,可以进行令牌窃取,例如,可以由用户模式进程触发。因此,任意写入足以执行代码,或者你可以解 SMEP 作为记录。

如今,当地址被随机化时,人们不知道在哪里分配 shellcode,因为 HalDispatchTable 是在每次启动时随机化的。

漏洞利用编写者变得更聪明,并且很快就明白你可以调用 NtQuerySystemInformation 或 EnumDeviceDrivers 来获取 ntoskrnl 的基地址。这样,我们就绕过了 KASLR,使任意写入对于 LPE 来说已经足够了。当然,只有当被控制的进程具有中等完整性(大多数进程都具有中等完整性)时,才可以调用这些API;浏览器不是。除此之外,使用 PageEntry 攻击覆盖 HalDisptachTable 是无效的,因为 HVCI 等新的缓解措施仍然不是大多数 Windows 设备上的默认设置。

或者,可以直接从系统进程读取数据。例如,可能会读取完全加载到内存中的 SAM 文件。现在,假设你可以从内存中提取它。在这种情况下,你可能可以破解哈希并找到管理员密码,从而使你可以悄悄地实现完全的权限提升。由于你并没有真正接触磁盘,因此不会触发防攻击。

那么信息泄漏是什么样的呢?它主要以获取某些敏感内核对象的内核指针的形式出现,但它也可能看起来像公开未初始化的内核内存,例如在 RtsPer.sys Realtek 驱动程序中:

17.png

图17-rts_ctrl_dump_mem_log 包含严重的信息泄漏漏洞

乍一看,这里提供的代码似乎很可靠;驱动程序使用 METHOD_BUFFERED(因为它以 00 结尾)来传输数据,所以用户数据不能从它的缓冲区分离出;缓冲区长度是可信的(详见图11和上面的内容)。然而,即使使用METHOD_BUFFERED,它也不能保证代码无漏洞。

查找信息泄漏漏洞的最简单方法是查看返回的数据及其长度。由于我们处理的是 METHOD_BUFFERED,因此返回的数据是通过 SystemBuffer 进行的,并且 IRP->IoStatus.Information 指定了大小(不仅针对此传输类型)。由于IoManager 并没有在输入缓冲区的副本之外初始化系统缓冲区。换句话说,如果 OutputBufferLength > InputBufferLength,则 SystemBuffer 的剩余部分是未初始化的数据。因此,驱动程序编写者的工作是初始化系统缓冲区并返回它的正确长度。

图16中的SystemBuffer没有被初始化为零。我们没有检查输入/输出缓冲区的长度,这是我们完全可以控制的。另外,通过查看else块,我们可以检查漏洞的逻辑:

18.png

图18-不正确的缓冲区大小分配

事实证明,通过进入 else 块,你从内核中得到了 0x107c0 字节长度的内核数据。更准确地说,IoManager 复制了 OutputBufferLength 和 InputBufferLength 之间的增量:

OutBufferLength = 0x107c0,InputBufferLength = 1

因此,我们从System进程的内存空间中返回0x107c0,大约有64k的未初始化数据,允许你读取SAM文件的内容,查找系统的内核基地址以及更多内容。此外,你可以根据需要多次触发此行为,因为它不会引发异常。

漏洞利用代码非常简单,我们唯一担心的是找到设备名称,因为它是一个自动生成的数字,但你可以暴力破解它,因为只有大约 0x200 选项,类似于图 2 中所示的方式。我们的漏洞利用代码可以执行以下操作:

打开 RtsPer.sys 公开的设备的句柄;

分配一个大小为 0x107c0 的缓冲区;

调用 DeviceIoControl;

将内核数据写入文件;

19.png

图19-在 RtsPer.sys 中滥用信息泄漏的简短代码

这只是我们在RealTek 最近修复的此驱动程序中发现的众多漏洞之一。

似乎这些漏洞很容易检测,也相对容易修复,你需要做的就是在 else 块中将 IRP->IoStatus.Information 更改为零。此外,你可以通过改变设备的权限来防止所有这些漏洞,只允许管理用户或以上用户与它交互。

第三类漏洞

我们要研究的第三种类型的漏洞会导致完全权限提升,除非它是从低完整性进程中执行的。在内核中发现的一个更好且通常更有用的漏洞是任意写入。这个原语允许你在内核中的任何地方进行写入,这可能会让你覆盖你的访问令牌。

如果你不记得或不熟悉 Windows 操作系统,每个进程都有一个对应的内核对象,称为EPROCESS。每个 EPROCESS 对象都有一个代表其安全上下文的访问令牌。 System 进程实际上是内核,自然拥有操作系统中最强大的访问令牌。由于每个 EPROCESS 都驻留在内核空间中,我们不能随意更改 EPROCESS 的访问令牌,我们必须从内核本身开始。但假设我们有一个任意的写入原语,则可以更改任何进程的访问令牌,并将其替换为系统的进程访问令牌,这种技术被称为纯数据( data-only )攻击。与内存攻击技术相比,我们不会损坏任何页面池条目,这些条目容易受到 kCFG/HVCI/HyperGuard 等新缓解措施的影响。

假设是寻找漏洞的关键

找到任意写入漏洞是很重要的,但对于完全的本地权限升级来说还不够。

假设HVCI开启:

无论你的完整性级别如何,内核中的任意写入对于 LPE 来说都是不够的;你还需要一个读取原语来启用令牌中的正确权限。

如果HVCI关闭:

完整性级别为中(最常见),可以调用 EnumDeviceDrivers 和 NtQuerySystemInformation 来获取内核的基地址,这应该允许你在某些页面条目损坏的情况下覆盖 HalDisptachTable/SMEP,这对 LPE 来说已经足够了。

完整性级别低或不受信任(主要是浏览器),你不能调用上面提到的 API,因此,没有适合你的内核基地址。必须有一个额外的 LPE 原语。

任意写入漏洞是什么样的?通常,它涉及内存的解引用和受控缓冲区的复制操作,它可以有多种形式,比如memcpy/memmove ,任意指针解引用等,让我们看看一些比较流行的:

20.png

图20-在一个我们无法评论的驱动程序中发现的普通任意写入示例

假设我们的驱动程序使用METHOD_NEITHER作为传输类型,并且我们看到输入或输出缓冲区加载到寄存器中,这是一个任意指针解引用。此时,寄存器RAX和RCX都指向用户模式缓冲区。当然,你可以完全控制它们的内容,并且你事先知道它们在内存中的位置,因为你已经创建了它们。由于我们可以完全控制寄存器,我们可以写入 RAX 指向的内存地址,RCX 指向的 shellcode 地址。换句话说,我们有一个 write-what-where 类型的漏洞或任意写入。

如果我们有METHOD_NEITHER传输类型,何时使用缓冲区以及以何种方式使用。图 19 中的这种模式有点少见,但仍不时出现。

攻击功能的来源

谈到任意写入漏洞时,我们不能不提到一个关键函数,这个函数通常是其根源:不正确地使用任何一个memcpy例程。驱动程序开发人员使用的memcpy函数或宏RtlCopyMemory在设计上是不安全的。它不处理越界写入或内存重叠,源或目标位于同一内存中。有一个函数处理内存重叠,它是memmove或宏rtlmovmemory。这仍然不能解决越界写入的问题。此外,任何一个参数的不正确使用都可能导致漏洞。

让我们检查一个驱动程序中一个很容易看到的漏洞,该漏洞应该是无名的:

21.webp.jpg

图21-有问题的驱动逻辑,看起来很糟糕

这里显示的反编译代码有点像Ida,这通常会使我们的工作容易得多,并且在区分IO_STACK_LOCATION结构成员(如果它们是union)时犯了很多错误。乍一看,这会给我们带来一些困惑。让我们忽略 ioctl 0x26DC03 出现的漏洞,即不正确使用 ProbeForRead,甚至不使用缓冲区。幸运的是,熟悉内核内部的每个人都可以看到事情看起来很奇怪:

22.png

图22-更正变量名称和类型

经典的可怕的漏洞

为了使它看起来更好,我们按ALT-Y来选择IO_STACK_LOCATION的正确联合成员的正确字段;让我们看看它是什么样的:

23.webp.jpg

图23-ida应该如何表示调度函数的反编译输出

更改后,我们可以看到反编译的输出看起来像普通的 C。我们对 memmove 进行了几次调用,这是我在查看调度函数时建议做的第一件事。SystemBuffer似乎是一个由源字段、大小字段和目标字段组成的结构,它们被盲目地提供给memmove方法,我称之为最好的安全性。我们可以看到有三个对memmove的调用,ioctl 0x26DC04表示在内核中对任意内容进行写入操作。相反,ioctl 0x26FC08允许从内核读取,因为它稍后将被分配给SystemBuffer(不在图22中)并返回给用户。总而言之,该驱动程序提供了所有读/写功能,这允许攻击者通过系统令牌升级为特权帐户。当然,HVCI与上述示例无关。唯一可用的是这个驱动程序不能从普通用户权限级别访问,只能从管理员和更高级别访问。因此,它限制了升级目的的有用性。然而,这个驱动程序带有灵活内核原语的签名驱动程序,仍可以被利用。

修复这个驱动程序意味着重写调度例程的整个逻辑。供应商没有这样做,而是改变了设备的ACL,以防止非管理员用户干扰它。

本文,我们介绍了如何开始寻找WDM驱动程序中的漏洞。最重要的事情就是获得设备句柄。接着,我们继续分析IRP_MJ_DEVICE_CONTROL的调度例程,然后查看imanager如何处理用户数据。之后,我们讨论了一些漏洞以及如何找到它们并利用它们。

本文翻译自:https://www.cyberark.com/resources/threat-research-blog/finding-bugs-in-windows-drivers-part-1-wdm如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论