首页
社区
课程
招聘
[原创]初学Windows内核漏洞之CVE-2011-2005
发表于: 2021-11-2 21:08 11388

[原创]初学Windows内核漏洞之CVE-2011-2005

2021-11-2 21:08
11388

初学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


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-3-9 09:41 被1900编辑 ,原因:
上传的附件:
收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//