CVE-2016-0040
漏洞成因
通过网上的资料可以了解到这是在nt!WmipReceiveNotifications
函数中使用未经初始化的栈数据而导致的漏洞。为了了解为什么会使用未经初始化的栈数据,我们来对比一下补丁前后的代码变化:
可以看到在补丁后,nt!WmipReceiveNotifications
函数并没有修改,同时在补丁后的系统上运行exp
发现并没有执行到nt!WmipReceiveNotifications
,因此考虑到可能是上层的分发函数加了补丁,在BinDiff
中观察上层函数的确进行了修改:
可以看到补丁后对传入缓冲区的第一个成员进行了校验,如果为0
,则不会调nt!WmipReceiveNotifications
,在wrk
阅读这两个函数的实现后发现被校验的这个成员为PWMIRECEIVENOTIFICATION->HandleCount
,结合wrk
中nt!WmipReceiveNotifications
的实现可以总结出漏洞成因:
- 这个未经初始化的栈变量里面储存的是
WmipGuidObjectType
类型的对象指针,这个栈变量是一个数组,但是由于我们从环三传来的WMIRECEIVENOTIFICATION
的结构中我们将PWMIRECEIVENOTIFICATION->HandleCount
置为0
,而内核层没有对这种情况进行判断,导致在句柄计数为零的情况下nt!WmipReceiveNotifications
中没有对这个栈数组进行赋值填充,并在随后的代码中引用位于其中的对象指针,而这个指针数据是之前存留在栈中的垃圾数据,如果内核栈中的数据能由我们控制,那么我们就有可能利用这个漏洞进行内核权限代码执行。 - 从下面
nt!WmipReceiveNotifications
的代码实现中可以清晰看到漏洞的成因,当传入的ReceiveNotification
结构中的HandleCount
为0
时会跳过对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
的地址,同时在环三调用DeviceIoControl
将ReceiveNotification->UserModeCallback.Handle
设置为环三的窗口过程,达到替换窗口过程的效果。
利用效果
参考
https://bbs.pediy.com/thread-246433.htm
https://j00ru.vexillium.org/2011/05/windows-kernel-stack-spraying-techniques/