该漏洞存在于win32k!vStrWrite01函数中,该函数在对BitMap对象中pvScan0成员所指向的像素区域进行读写的时候,没有判断读写的地址是否已经越界,即超过了BitMap对象的像素点范围,导致BSOD的产生。通过合理的内存布局,可以利用该漏洞扩大目标BitMap对象的sizlBitmap来扩大该BitMap对象的可读写范围,利用此时被扩大读写范围的BitMap对象来修改另一个BitMap对象的pvScan0就可以实现任意地址读写。
操作系统:Win7 x64 7601 专业版
编译器:Visual Studio 2017
调试器:IDA Pro, WinDbg
该漏洞的POC代码如下:
POC代码通过CreateComatibleBitmap对象来创建了一个BitMap对象用来触发漏洞,该函数定义如下:
漏洞触发函数则是DrawIconEx,该函数用于在指定的设备上下文中绘制图像,该函数定义如下:
编译运行POC,系统就会产生BSOD错误,以下的部分错误信息:
根据错误信息可以知道,产生BSOD错误的代码地址位于win32k!vStrWrite01偏移0x36A处,产生原因是对不合法的地址,即0地址进行读取操作。
在NT4源码中可以找到vStrWrite01函数的定义如下:
其中,与漏洞相关的前三个参数定义如下:
根据WinDbg的错误信息可以在IDA中定位到触发错误的代码,在触发错误的代码上面不远处就可以看到,读取的内存地址由rcx与rax决定,所以就需要分析rcx与rax的计算才能知道读取的内存地址。
在vStrWrite01函数首先对参数进行赋值,并判断相关参数是否条件,这里的三个跳转都不会进行:
将r11d减一,也就是将保持的prun->xrl->cRun减一,之后就开始计算rcx和rax,在用rcx和rax来计算r14,即计算之后要读取的内存地址:
接下去执行就是导致BSOD错误产生的mov esi, [r14]这条指令。 如果r14中保持地址是合法的地址,之后就会对读取到的值进行or运算或者and运算:
把运算以后得到的值在赋值回r14指向的内存地址:
对rcx进行增加,在判断r11d是否为0,如果不为0就会跳转到loc_FFFFF97FFF0A5423,也就是跳转到上面的sub r11d, r12d指令开始,再次执行上述的这些指令:
根据上面分析,可以得出要读写的内存地址,即r14寄存器的值的计算可以用以下循环来计算:
漏洞利用的步骤如下:
创建用来触发漏洞的BitMap对象hExpBitMap
在hExpBitMap偏移0x100070000处放置一个BitMap对象,作为hManager
在hManager之后偏移0x7000地址处分配一个BitMap对象作为hWorker
触发漏洞,扩大hManager所对应的BitMap对象的sizelBitmap来扩大hManager的可读写范围
通过hManager修改hWorker对应的BitMap对象的pvScan0,就可以实现任意地址读写实现提权
为了成功创建用于利用的hManager和hWorker,需要通过喷射大量0x7000大小的BitMap对象,相应的代码如下:
编译运行程序,在触发漏洞之前,hManager对应的BitMap对象的sizlBitmap的值如下:
触发漏洞之后,就可以看到可读写的范围被成功的扩大:
成功扩大hManager的读写范围之后,就可以通过修改hWorker的pvScan0来实现任意地址读写,最终实现提权,相应代码如下:
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课