Windows WDM 驱动漏洞挖掘(上)
来源:http://www.tudoupe.com时间:2022-06-28
每个驱动程序中的漏洞基本上是Windows内核中的漏洞,因为每个驱动器共享内核的内存空间。在内核中运行代码、读取、写入或从模型注册表复制特权访问许可证的能力实际上属于系统。本文介绍了WDM驱动程序中的漏洞检测方法,然后通过kAFL使用内核模糊性。大多数漏洞都出现在WDM或KMDF中。
在博客的每个部分,我们将从基本方面开始,例如熟悉相关API和数据结构。
WDM
Windows驱动程序模型(WDM)是最古老的,它也是最常用的驱动框架。每个驱动程序基本上是WDM驱动程序;较新的框架WDF(Windows Driver framework)包含WDM。简化了WDM的开发过程,它解决了WDM的许多技术困难.检查WDM驱动程序时,我们主要关心的是如何与他们沟通;几乎所有驱动程序中的漏洞都涉及一些非特权用户与驱动程序本身的通信。
在例子中,这是名为“testy”的驾驶员的进入点:
经典的DriverEntry代码没有FILE_DEVICE_SECURE_OPEN符号来调用IoCreateDevice
这个代码是每个WDM驱动程序所拥有的 DriverEntry函数的共同架构。第一个参数是驱动对象结构指针,用于启动设备的创建和调度程序.接下来,司机有 MajorFunction成员,这是一个函数指针的集合,用于分配不同事件的日程表。此外,我们还将为关键设备创建一个程序,将在下一个节中介绍。
设备创建和初始化
驱动程序首先通过呼叫IoCreateDevice创建设备,这将在对象管理器中创建DEVICE_OBJECT。在Windows中,设备对象代表驱动程序处理I/O请求的逻辑、虚拟或物理设备。这一切听起来不错。但是如果我们希望它能从普通用户的角度来沟通,这还不够;为此,我们叫IoCreateSymbolicLink,它将在对象管理器中创建DoS设备名称,允许用户通过设备与驱动程序进行通信。但是,有些设备没有正常名称,它们有一个自动生成的名称(在PDO中完成)。对不经验的侦探来说,他们可能看起来很奇怪,所以如果你第一次看到它们在你最喜欢的设备中,请查看软件,在设备名称列中查看8位16英寸输入。这些设备可以像所有其他命名设备一样相互作用。
显示WinObjEx设备命名空间
在设备创建实例中最重要的事情是程序员是否将ACL和 DeviceCharacteristics值分配给设备。
不幸的是,IoCreateDevice方法不允许程序员指定任何ACL,这是不好的。因此,开发者必须在注册表或驱动程序inini文件中定义ACL。如果他们不能做到,任何用户都可以访问该设备。然而,使用IoCreateDeviceSecure方法可以缓解这种情况。
除此之外,我们还需要看第五个参数, Device Characteristics。如果 DeviceCharacteristics值不执行OR操作,则0x00100或FILE_DEVICE_SECURE_OPEN,我们可能面临安全漏洞(除非我们讨论文件系统驱动程序或支持名称结构的任何驱动程序)。原因在于Windows处理设备的方式;每个设备都有自己的命名空间。设备名称空间中的名称是与设备名称开始的路径。对于名为DeviceDeviceName的设备,它的命名空间由任何名称组成,以"DeviceDeviceNameanyfile"的形式。
如图1所示,没有FILE_DEVICE_SECURE_OPEN符号的 ioCreateDevice调用意味着设备ACL不适用于打开设备命名空间中的文件请求。换句话说,即使我们通过IoCreateDeviceSecure或其他方法创建设备时指定强的ACL,ACL不会用于打开文件请求。结果,我们没有真正得到我们想要的,所以使用Devicetestydrv来调用CreateFile将失败,但是使用"devicetestydrvanyfile"的呼叫会成功,因为IoManager不应用设备ACL来创建请求(因为它假设它是文件系统驱动程序)。对于初学者来说,它被认为是值得修复的一个脆弱性。此外,这将使非管理员用户尝试读写设备,执行 DeviceIoControl请求等等,这通常是您不想让非管理员用户做的事。
更好的用户保护
我们可以叫IoCreateDeviceSecure(或WdmlibIoCreateDeviceSecure;它也是相同的功能),使用安全描述符防止非管理员用户打开设备手柄,在创建实例时使用FILE_DEVICE_SECURE_OPEN值。这样,我们还可以在登记册上标明设备许可的麻烦,就像我们需要在IoCreateDevice中。
我们应该如何制造设备?
从寻找漏洞的视角来看,我们应该列出系统中的每一个可能的设备,然后尝试用GENERIC_READ | GENERIC_WRITE打开它,这允许我们筛选不能与它通信的设备。
调度方法
创建设备很好,但与司机沟通是不够的,还需要 IRP。驱动程序代表IoManager接收特定触发器IRP和I/O请求包。例如,如果应用程序试图打开设备手柄,IoManager将调用分配给驱动对象分配的相应的调用方法。因此,它允许每个驱动程序为每个它创建的设备支持多个不同的 MajorFunctions。有大约30种不同的主要功能。如果IRP_MJ_PNP_POWER被使用,每个代表一个不同的事件。我们将只关注两个主要功能方法,并附上其他方法的简要说明,在寻找漏洞时,我们应该注意这一点。
基本驱动程序日程分配
调用IRP_MJ_CREATE
在我们深入研究最有趣的目标之前,IRP_MJ_DEVICE_CONTROL,我们将从IRP_MJ_CREATE开始。每个内核模式驱动程序必须在驱动程序的回调函数中处理IRP_MJ_CREATE。驱动程序必须执行IRP_MJ_CREATE,因为没有它,无法打开设备或文件对象的手柄。
正如你可能猜到的,当您调用NtCreateFile或ZwCreateFile时,您将调用IRP_MJ_CREATE调用程序。在大多数情况下,它将是一个空存根,并且根据设备的ACL返回一个与请求的 DesiredAccess接口。
典型的强制执行方法
但是,在某些情况下,将涉及更复杂的代码,即使你符合设备的ACL标准,您可能也得到类似的Status_INVALID_PARAMETER状态漏洞,因为您在调用NTCreateFile时使用了错误参数。
不幸的是,这意味着你不能盲目打开设备,您需要通过 DeviceIoControl与驱动程序进行通信,首先需要了解其预期参数。通常,DispatchCreate需要一些ExtendedAttributes(而不是通常的CreateFile)或特定的文件名(不包括设备名)。因此,我们必须访问发送创建方法.
显示是否存在一个名为"StorVsp-v2"的扩展属性和值字段的长度为0x19字节。 因此,驱动程序是StorVsp.sys
除了打开手柄外,您可以在 dispatchCreate中搜索漏洞。 功能越复杂,存储分配和释放漏洞就越有可能发生,特别是因为DispatchCreate不经常被检查。
在寻找驾驶员的脆弱性时,我们采取的一般方法是:
枚举每个设备对象:
尝试使用最简单的 DesiredAccess来打开它;
如果它失败,请检查状态代码;如果它不是Status_ACCESS_DENIED,您仍然可以通过做一些手动工作和更改一些参数打开手柄;
通过遵循这个简单的算法,我们将有大约70台设备的清单,我们可以从非经理的观点来看他们。当然,这个数字取决于不同的Windows设备,因为OEM驱动程序和许多类型的软件也会安装驱动程序。
使用ioctls控制设备
虽然ioctls很少允许您完全控制设备/驱动程序,但实际上是应用程序与驱动程序通信的方式。 驱动程序可以创建两个ioctl调度程序:
设备控制方法的典型用途
唯一重要的方法是TestyDispatchIoctl,由于我们不能以任意参数调用IoBuildDeviceIoControlRequest或IoAllocateIrp,这是触发IRP_MJ_INTERNAL_DEVICE_CONTROL主函数的函数。如果是,这是因为内部调度方法很少经过适当的测试。
与 DriverObject的任何调用方法一样,它从IoManager中接收两个参数。
WDM驱动器中的每个调用方法共享相同的函数签名
第一个是我们执行创建文件操作的设备对象,第二个指的是指向IRP的指示器。从脆弱性研究的角度来看,IRP包含用户数据和其他我们根本不关心的事情。我们主要关心的是在用户模式中发送哪些参数。如果我们看看NTDeviceIoControlFile的签名,在寻找驱动程序中的漏洞时,我们可以猜出我们关心哪些领域:
DeviceIoControl API
该方法的主要问题是输入/输出缓冲器、它们的长度和Ioctl代码本身。我们从Ioctl代码开始,它是作为注释的32位数;它描述了缓冲区和长度如何使用/再生成到内核,需要 DesiredAccess(当你打开设备手柄时)和功能指示器。示例如下:
FileTest工具的图像显示32IOctl编码的比特率
我们可以看到ioctl代码为0x100,它的意思是:
设备类型: FileDevice_0:与我们无关;
这与我们无关;
它与我们有关,因为它描述了imanager如何将数据转移到内核;
访问:FILE_ANY_ACCESS:它与我们有关,因为它定义了您需要拥有手柄的访问权限。如果你没有权利进入,因此,IoManager不允许调用并返回AccessDenied。有四个不同的值:
FILE_ANY_ACCESS:无论是否设置 DesiredAccess参数,您总是有设备手柄;
FILE_READ_DATA:您使用GENERIC_READ请求一个手柄,并得到有效的手柄;
FILE_WRITE_DATA:使用GENERIC_WRITE请求一个手柄,得到有效的手柄; FILE_READ_DATA | FILE_WRITE_DATA:不要大声说,你需要这两个权利;
在 DeviceVfpExt 手柄上运行 DeviceIoControl 请求将导致 BSoD, 不管您的权限级别, 在理解图3中的方法字段后, 我们将看到为什么。
Method/TransferType
方法/转移类型被称作邪恶的母亲,这听起来有点夸张,但不幸的是,事实确实如此。传输类型的方法,即,ioctl32的两个最低有效位数,指示IoManager如何引用内核中的参数(缓冲区和长度)。与访问字段一样,有四个不同的选项:
(1) METHOD_NEITHER,两个职位都是开放的:IOManager是无动的,不要检查缓冲区及其长度。缓冲区不会复制到驱动程序,并留在用户模式中。因此,用户可以任意操纵缓冲器的长度,并释放/分配其页面,这会导致许多坏事:系统崩溃和功率升级,除非缓冲器被正确检测。如果你看到没有缓冲检测的驱动程序,相反,使用 method_neitHER,那肯定存在漏洞。
(2) METHOD_BUFFERED,没有空位:IoManager将输入/输出缓冲区及其长度复制到内核,这使得它更加安全,因为用户不能随意改变缓冲器或改变其内容和长度。之后,输入/输出缓冲指针被分配给IRP。
(3)METHOD_IN_DIRECT和(4)METHOD_OUT_DIRECT中的两个位置之一是开放的:两者都非常相似;imanager将分配输入缓冲区为METHOD_BUFFERED。对于输出缓冲区,IoManager检测缓冲器,并检查虚拟地址在当前访问模式下是否可写入或可读。然后,它锁定存储页并将指针传递到IRP上。
让我们看看驱动程序如何访问用户模式缓冲器,并看到一个快速的漏洞,表明驱动程序没有适当的安全检查。
这里我们可以看到如何将驱动器中的每个缓冲区与描述传输方法和类型的Ioctl代码联系起来
因为驱动程序通常可以支持多个ioctl代码,所以对于每个不同的ioctl代码,它有一个大开关箱,影响存储在内存中的缓冲器的位置。在下一节中,如果我们不注意,我们就会看到什么。
参考文献: https://ww.Cyberark.Finding-bugs-in-windows-drivers-part-1
下一篇:没有了
相关新闻
- 2022-06-28 Windows DHCP服务器原理
- 2022-06-28 紧跟IE脚步,微软为Windows 8.1宣告“
- 2022-06-28 强大的自由现金流或令微软股价再
- 2022-06-28 微软给iPhone做了个杀毒App,结果被
- 2022-06-27 紧跟IE脚步,微软为Windows 8.1宣告“
- 2022-06-27 做一个国产操作系统到底有多难?
- 2022-06-27 美股早知道:微软公司涨3.42%,Me
- 2022-06-27 强大的自由现金流或令微软股价再
- 2022-06-27 微软Edge浏览器WebView2程序被指可用
- 2022-06-27 微软Edge浏览器WebView2程序被指可用
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
