CVE-2016-0040

CVE-2016-0040

漏洞成因

通过网上的资料可以了解到这是在nt!WmipReceiveNotifications函数中使用未经初始化的栈数据而导致的漏洞。为了了解为什么会使用未经初始化的栈数据,我们来对比一下补丁前后的代码变化:

可以看到在补丁后,nt!WmipReceiveNotifications函数并没有修改,同时在补丁后的系统上运行exp发现并没有执行到nt!WmipReceiveNotifications,因此考虑到可能是上层的分发函数加了补丁,在BinDiff中观察上层函数的确进行了修改:

可以看到补丁后对传入缓冲区的第一个成员进行了校验,如果为0,则不会调nt!WmipReceiveNotifications,在wrk阅读这两个函数的实现后发现被校验的这个成员为PWMIRECEIVENOTIFICATION->HandleCount,结合wrknt!WmipReceiveNotifications的实现可以总结出漏洞成因:

  • 这个未经初始化的栈变量里面储存的是WmipGuidObjectType类型的对象指针,这个栈变量是一个数组,但是由于我们从环三传来的WMIRECEIVENOTIFICATION的结构中我们将PWMIRECEIVENOTIFICATION->HandleCount置为0,而内核层没有对这种情况进行判断,导致在句柄计数为零的情况下nt!WmipReceiveNotifications中没有对这个栈数组进行赋值填充,并在随后的代码中引用位于其中的对象指针,而这个指针数据是之前存留在栈中的垃圾数据,如果内核栈中的数据能由我们控制,那么我们就有可能利用这个漏洞进行内核权限代码执行。
  • 从下面nt!WmipReceiveNotifications的代码实现中可以清晰看到漏洞的成因,当传入的ReceiveNotification结构中的HandleCount0时会跳过对ObjectArray的初始化,但在后续代码仍然可能发生对ObjectArray的使用,如果ObjectArray的值能够被控制,那我们将拥有一个任意地址任意写。
 NTSTATUS WmipReceiveNotifications(
	PWMIRECEIVENOTIFICATION ReceiveNotification,
	PULONG OutBufferSize,
	PIRP Irp
 )
{
		
	......
 
	for (i = 0; (i < HandleCount); i++)
	{
	 
		Status = ObReferenceObjectByHandle(HandleArray[i].Handle,
                                       WMIGUID_NOTIFICATION,
                                       WmipGuidObjectType,
                                       UserMode,
                                       &GuidObject,
                                       NULL);
 
		......

		ObjectArray[ObjectCount++].GuidObject = GuidObject;        

		......			
	}

	if (ReceiveNotification->Action == RECEIVE_ACTION_CREATE_THREAD) 
	{
		GuidObject = ObjectArray[0].GuidObject;
        GuidObject->UserModeCallback = (PUSER_THREAD_START_ROUTINE)(ULONG_PTR)ReceiveNotification->UserModeCallback.Handle;
        GuidObject->EventQueueAction = RECEIVE_ACTION_CREATE_THREAD;
        GuidObject->UserModeProcess = UserModeProcess;
        GuidObject->StackSize = StackSize;
        GuidObject->StackCommit = StackCommit;
		
		......
	}

	......

	return(Status);
}

漏洞利用

  • 内核栈喷射:在网上找到一篇j00ru《nt!NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques》文章,可以通过NtMapUserPhysicalPages 从环三向内核栈中布置0x400 * siezof(ULONG_PTR)大小的数据,从而可以稳定的覆盖ObjectArray的位置,具体细节可以参考上面这篇文章。

  • 在上面的nt!WmipReceiveNotifications伪代码中我们可以看到任意写的值可以由我们控制的是ReceiveNotification->UserModeCallback.Handle,这个值可以由环三我们调用DeviceIoControl时控制。

  • 现在利用的思路是在Win7 sp1上通过泄露窗口tagWND->lpfnWndProc 的地址,然后通过NtMapUserPhysicalPages 向内核栈中大量喷射tagWND->lpfnWndProc 的地址,同时在环三调用DeviceIoControlReceiveNotification->UserModeCallback.Handle设置为环三的窗口过程,达到替换窗口过程的效果。

利用效果

参考

https://bbs.pediy.com/thread-246433.htm

https://j00ru.vexillium.org/2011/05/windows-kernel-stack-spraying-techniques/

猜你喜欢

转载自www.cnblogs.com/DreamoneOnly/p/13163036.html