-
-
[原创]初学Windows内核漏洞之CVE-2011-2005
-
发表于: 2021-11-2 21:08 11431
-
初学Windows内核漏洞,选择了比较早的CVE-2011-2005 Windows Afd.sys本地提权漏洞来试着分析学习,作为学习笔记分享给大家。
在2011年的11月,微软补丁公告提到,Windows系统中的辅助功能驱动程序Afd.sys存在本地提权漏洞,影响Windows XP与Windows Server 2003系统。该漏洞主要是Microsoft Windows Ancillary Function Driver(afd.sys)驱动程序未对用户提交的数据进行完成地检查,导致存在本地提权漏洞,攻击者利用该漏洞可执行任意代码。
以下是POC中的部分代码
根据上面的部分代码可以得出下面的结论:
程序通过socket与驱动程序进行通信,IP地址为127.0.0.1,端口为4455
触发这个漏洞的IOCTL为0x00120BB
而IO控制码的计算是由CTL_CODE完成的,它是一个宏定义,在文档中的定义如下
可以知道控制码最低的两位保存的是用户层和内核层的通信方式,而触发漏洞的控制码的低两位是11(0x3),根据文档中的如下定义可以知道,此次通信方式为METHOD_NEITHER
而第2位到第14位保存的是发送的操作码,1000 0010 1110(0x82E)
首先看看afd驱动在系统中装载情况:
可以看到,该驱动在内核地址0xB25D3000到0xB25F4D00。
再看看该驱动在分发函数的情况:
可以看到IRP_MJ_DEVICE_CONTROL对应的分发函数是AfdDispatchDeviceControl,函数地址是0xB25DE290。那么接下来就可以在IDA中定位到这个分分发函数。
而要真正理解这个分发函数,需要了解以下的数据结构
首先是_IRP结构,该结构在WinDbg中查看可以得到如下结果
这里需要注意的是偏移0x60的CurrentStackLocation,保存的是_IO_STACK_LOCATION的指针,代表了当前设备的I/O栈。
而_IO_STACK_LOCATION的部分结构体成员如下
在这个结构体中要关注的是偏移为0x04的Parameters,它保存的是一个联合体,其中的一个是DeviceIoControl。DeviceIoControl中又保存了4个成员,这4个成员的含义如下
有了上面的基础应该就可以理解AfdDispatchDeviceControl的反汇编代码,
这段代码就是取出IOCTL并计算保留了低9位的控制码。由于POC给出的操作码是(0x82E),所以这里得到的是0x2E,这个数字就作为索引去整型数组_AfdIrpCalldispatch中取出要执行的函数的地址。
根据偏移可以算出这个函数是AfdJoinLeaf,所以产生漏洞的函数就是这个函数。
继续跟进这个函数查看漏洞成因,但要注意此时的edx保存的依然是CurrStackLocation,ecx保存的是IRP的指针,即pIrp。
函数最开始就对输入缓冲区和输出缓冲区进行判断,如果输入缓冲区的长度小于0x18或者输出缓冲区的长度大于0小于0x8,就会跳转到loc_1716E处,而这个地方的代码是函数执行失败以后的退出代码,如下
那么接着看函数正常执行,也就是loc_16CD2的代码,这段代码主要做的就是对输入缓冲区进行检查。如果地址非法,程序就会抛出异常
接下来的代码就是对局部变量进行赋值。这里需要注意的是
Handle变量,它的值是输入缓冲区偏移为0x8的地址中的内容
InputBufferLength变量,保存了输入缓冲区的长度
esi保存了输入缓冲区0x0C偏移的地址
然后程序就申请了一块内存,这块内存的大小是0x30加上输入缓冲区的长度-0x0C的大小。注意此时eax在上面有减去0xC的大小。随后程序就将申请到的内存的前0x30的大小初始化为0,在把输入缓冲区0x0C开始的内容复制到申请的内存偏移0x30的地址开始处。
接着函数判断eax中保存的内容是否等于1,不等于1则抛出异常。因为这个时候eax保存的是edx+0x30,也就是申请的内存地址偏移0x30处的地址。所以eax中保存的内容是输入缓冲区偏移0x0C处的内容,那这里就是判断输入缓冲区偏移0x0C保存的是否是1。
随后在判断edx是否大于等于eax,不是的话继续向下执行抛出异常。而这里的ecx的值是上面输入缓冲区长度减去0xC,而edx经过分析则是输入缓冲区偏移0x10处保存的内容在加8的值。也就是说输入缓冲区的长度减去0xC要大于等于输入缓冲区偏移0x10处保存的内容加8。
接下去的内容就是该产生该漏洞的内容。在这段代码中,程序首先会判断输出缓冲区的长度是否为0,为0的话则跳转到loc_16DC5继续执行。如果不为0,程序才会接着对UserBuffer的地址进行合法性检查。
接着程序会调用ObReferenceObjectByHandle,而这个Handle参数在前面被赋值为输入缓冲区+0x8的内容。由此可知,输入缓冲区+0x8的地址中的内容不可以为空。以及,此时要记得esi保存的是FsContext,而FsContext保存的是套接字的状态。
在后面程序就会对套接字的状态进行检查,如果不是CONNECTING状态,则会触发异常。
虽然在AfdJoinLeaf中存在了不对UserBuffer地址进行合法性检查的漏洞,但是对该值进行赋值操作的代码却不再这个函数中。它在以下的AfdRestartJoin函数中
该函数将会调用AfdConnectApcKernelRoutine来执行代码
在该函数中可以看到,程序将IRP的状态赋给了UserBuffer中保存的地址
由上面分析可以得出下面的结论,在AfdJoinLeaf中最终会调用到afdConnectApcKernelRoutine,在这个函数中会对UserBuffer的地址赋值为IRP的状态。而要调用到这个函数则需要满足下面的条件
输入缓冲区的长度必须大于0x18
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]CVE-2022-21882提权漏洞学习笔记 16458
- [原创]CVE-2021-1732提权漏洞学习笔记 19581
- [原创]CVE-2014-1767提权漏洞学习笔记 15227
- [原创]CVE-2018-8453提权漏洞学习笔记 18587
- [原创]CVE-2020-1054提权漏洞学习笔记 13581