以 CVE-2024-26229 为例分析 Windows RDBSS 机制
一、前言 CVE-2024-26229是今年4月微软修补的一个任意地址写零漏洞,该漏洞位于csc.sys驱动中。攻击者在Windows上获得较低权限的任意代码执行后,可以利用该漏洞将低权限提升至system权限。目前Exp由varwara公布在了github上。 二、csc.sys简介 csc.sys(Windows Client Side Caching Driver)是脱机文件(offline files)功能的一部分。脱机文件功能是默认在本机存一份共享文件的副本,这样做的优点有两点,一方面可以节省网络流量,另一方面是断网条件下,仍然可以使用共享文件夹,等到有网之后再进行同步。默认副本文件存储在C:\Windows\CSC文件夹中,普通用户没有权限访问该文件夹的内容。 下面以SMB协议为例,演示如何开启脱机文件功能。 首先准备两台虚拟机,一台为SMB server,一台为SMB client。 在SMB Server首先新建一个文件夹,然后共享此文件夹,并设置此权限为完全控制。 在SMB client打开控制面板进入同步中心->管理脱机文件->启动脱机文件。 然后访问SMB server,点击始终脱机可用。 之后就会出现一个新的文件夹,即为本机副本。 此时关闭share文件夹分享,仍然能操作share文件夹。 三、漏洞成因分析 通过README可以看出该漏洞产生的原因是在csc.sys中没有校验用户态以METHOD_NEITHER方式和驱动进行通信时用户态传入的缓冲区地址是否是合法地址,从而导致任意地址读或者任意地址写。 下面介绍一下windows用户态与驱动的通信方式来解释为什么以METHOD_NEITHER与驱动进行通信时需要对用户态传入的地址进行合法校验。 在windows中用户态与驱动的通信方式共有三种: METHOD_BUFFERED METHOD_IN_DIRECT METHOD_OUT_DIRECT METHOD_NEITHER METHOD_BUFFERED方式对UserInputBuffer和UserOutputBuffer都进行缓冲,驱动程序无需对用户态传入的缓冲区地址进行校验。 METHOD_IN_DIRECT和METHOD_OUT_DIRECT只对UserInputBuffer进行缓冲,对于UserOutputBuffer采用的是将用户态地址锁定(即不让其换出内存),然后映射为内核地址。在驱动写入后,重新映射为用户态地址。 METHOD_NEITHER (也就是出现漏洞的这种通信方式) 驱动程序直接读写用户态的缓冲区,优点是读写更快,缺点是驱动程序在读写缓冲区之前需要使用ProbeForRead/ProbeForWrite函数去探测地址是否合法。就可能会出现漏洞,例如用户态的传入的UserInputBuffer和UserOutputBuffer均为内核态地址,驱动就会根据用户态传入地址读写,即可造成任意地址读写。 四、补丁分析 可以看到有两个函数有改动,既然本漏洞是关于I/O control的,那么优先看CscDevFcbXXXControlFile这个函数改动。 通过bindiff可以看到从00000001C006B243这个块开始有所不同,定位到对应的反编译的伪c代码进行比较。 在patch之后的代码加了一个if判断 如果if 判断条件为true,则首先在使用前判断v12是否小于0x24 如果小于0x24,则调用失败 如果大于等于0x24,则会判断(a1+40)+64的是否不为0,这里推测如果为1的话,应该表示该请求来自用户态 如果不为0,则会使用ProbeForWrite探测a1+536的地址是否合法 然后对a1+536的位置写入一个8字节的0 (这里可能就是未patch的漏洞点了) 如果if判断条件为false,则使用原来的漏洞代码 patch代码保留原来漏洞代码目的推测为微软可能认为新代码可能会影响正常的功能,一旦遇到问题可以只需要改一下某个标志位就可以回滚回原来的代码,等到新代码完全稳定后估计就会删除原来的漏洞代码。 五、漏洞触发流程分析 从目前的分析来看,这个漏洞模式比较简单,为什么之前没有被人扫描到或者分析出来,我认为有两点原因。 ida反编译显示错误,有的时候你看csc.sys驱动的DriverEntry函数,可能如下图所示: 如果你多次Undefine然后重新反编译DriverEntry,就会发现DriverEntry函数是很长的。 csc.sys是一个内核网络微型重定向器驱动程序(Kernel Network Mini-Redirector Driver)使用了重定向驱动器缓冲子系统 (Redirected Drive Buffering Subsystem) 使得处理DeviceIoControl调用栈过深,不易被发现。 由DriverEntry可以看到用户态所有请求都由CscFsdDispatch处理,跟进CscFsdDispatch函数可以发现大部分的请求都是由导入函数RxFsdDispatch处理 RxFsdDispatch是由rdbss.sys中实现,下面介绍以下csc.sys与rdbss.sys交互为例介绍rdbss机制。 5.1 rdbss机制分析 csc.sys与rdbss.sys的交互分为三个状态,并且是顺序的。 5.1.1 csc.sys在初始化时调用RxRegisterMinirdr()初始化PRDBSS_DEVICE_OBJECT的部分成员 RxRegisterMinirdr函数声明如下: NTSTATUS RxRegisterMinirdr( [out] OUT PRDBSS_DEVICE_OBJECT *DeviceObject, [in, out] IN OUT PDRIVER_OBJECT DriverObject, [in] IN PMINIRDR_DISPATCH MrdrDispatch, [in] IN ULONG Controls, [in] IN PUNICODE_STRING DeviceName, [in] IN ULONG DeviceExtensionSize, [in] IN DEVICE_TYPE DeviceType, [in] IN ULONG DeviceCharacteristics ); 这里主要关注MrdrDispatch这个参数,这个参数指向网络微型重定向驱动的调度表的指针。此调度表包括网络微型重定向驱动的配置信息和指向由网络微型重定向器内核驱动程序实现的回调例程的指针表。RDBSS 通过此回调例程列表调用网络微型重定向器驱动程序。...