[原创]cve-2016-0051简要分析
cve-2016-0051为基于webdav的windows本地提权漏洞
先大概描述一下漏洞。
漏洞是在mrxdav.sys这个驱动程序里面的mrxdav!MRxDAVDevFcbXXXControlFile函数。
在发送畸形的web请求时候,这个函数里面验证不严的话会调用这个函数MRxDAVCreateContinuation,大概是在MRxDAVCreateContinuation+0x254位置处。会造成固定地址写入固定值类型的漏洞,具体看ida的代码吧。
-----------------------------------------------------------------------------------------------------
代码片段1:
DevObj__ = *(v5 + 104);
if ( !*(pRelevantSrvOpen + 32) )
{
v21 = ExAllocatePoolWithTag(v14, 0x40ui64, 0x6F535644u);
*(pRelevantSrvOpen + 32) = v21;
if ( !v21 )
{
v8 = -1073741670;
v18 = &WPP_GLOBAL_Control;
if ( WPP_GLOBAL_Control == &WPP_GLOBAL_Control )
return v8;
if ( !_bittest(WPP_GLOBAL_Control + 11, 0xDu) )
goto LABEL_254;
LODWORD(v19) = PsGetCurrentThreadId(v17, v16);
v20 = 61i64;
goto LABEL_15;
}
memset(v21, 0, 0x40ui64);-------------------------------------------------------------------------------------------------
代码片段2:
if ( !*(*(pprx_context + 80) + 40i64) )
{
*(*(pprx_context + 80) + 40i64) = ExAllocatePoolWithTag(PagedPool, 0x820ui64, 0x6F465644u);
v100 = *(*(pprx_context + 80) + 40i64);
if ( v100 )
{
memset(v100, 0, 0x820ui64);
}..........
-------------------------------------------------------------------------------------------------------
代码片段3:
v68 = ExAllocatePoolWithTag(PagedPool, 0x40ui64, 0x69465644u);
v31 = 0;
v9 = v68;
if ( !v68 )
{
v8 = -1073741670;
v17 = &WPP_GLOBAL_Control;
if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control &&
_bittest(WPP_GLOBAL_Control + 11, 0xDu) )
{
LODWORD(v69) = PsGetCurrentThreadId(&WPP_GLOBAL_Control, v16);
WPP_SF_qd(*(WPP_GLOBAL_Control + 3), 65i64, &qword_3207580, v69);
}
goto LABEL_245;
}
memset(v68, 0, 0x40ui64);----------------------------------------------------------------------------------------------------
就是v100这个变量,这是rx_context结构体的 PMRX_SRV_OPEN pRelevantSrvOpen字段,32位的环境是偏移0x38,64位的是0x58,这个东西的有一个pdevice_object的双向链表,64为的是在0x20偏移位置处。
在这里是给链表的第一个节点置0了,这个双向链表反复赋值至零的作用下最后导致pRelevantSrvOpen的设备链表的第二个节点为pdevice_object=o;我下写入断点跟踪这个位置的写入情况是在代码片段1,也就是:
fffff880`03442cc8 fffff880`0320f2cc mrxdav!memset+0x97 fffff880`03442cd0 fffff880`032222a1 mrxdav!MRxDAVCreateContinuation+0x254 fffff880`03442e50 fffff880`0320d6a8 mrxdav!UMRxAsyncEngOuterWrapper+0x199 fffff880`03442eb0 fffff880`052eb532 mrxdav!MRxDAVCreate+0xbc fffff880`03442ef0 00000000`00000000 0xfffff880`052eb532
在把设备对象指针指向0后,就可以在0处写我们做的假的设备对象,通过NtFsControlFile再次调用这个程序就调用我们做的假的设备对象里面的假的驱动irp函数,就可以执行任意代码了
MRxDAVDevFcbXXXControlFile具体的rx_context结构的校验也没有细看,只是做了下补丁的对比,大概的感觉的是在看进程的eprocess的token时候的判断时候少判断了一种情况,补丁后的mrxdav!MRxDAVDevFcbXXXControlFile,专门开了个MRxDavIsCallerPrivileged函数做令牌获取。
-------------------------------------------------------------------------------------------------------
说一下调试方案吧:
把样本直接放在64位的win7里面跑,会在 mrxdav!MrxDAVEfsControl里面崩掉
栈:
fffff880`035d7180 fffff880`0320b56b mrxdav!MrxDAVEfsControl+0x477
fffff880`035d7250 fffff880`052e9b99 mrxdav!MRxDAVFsCtl+0x93
fffff880`035d7290 fffff880`052ef025 rdbss!RxLowIoSubmit+0x291
fffff880`035d72f0 fffff880`052eec15 rdbss!RxLowIoFsCtlShell+0x1c5
fffff880`035d7360 fffff880`052c7684 rdbss!RxCommonFileSystemControl+0xe45
fffff880`035d74b0 fffff880`052e4b44 rdbss!RxFsdCommonDispatch+0x870 创建RX_CONTEXT
fffff880`035d75a0 fffff880`03212b89 rdbss!RxFsdDispatch+0x224 和硬件联系用的驱动
fffff880`035d7610 fffff880`01bb3271 mrxdav!MRxDAVFsdDispatch+0x6c0 分发那种irp
fffff880`035d76e0 fffff880`01bb1138 mup!MupiCallUncProvider+0x161
fffff880`035d7750 fffff880`01bb29e3 mup!MupStateMachine+0x128
fffff880`035d77a0 fffff880`01098bcf mup!MupFsControl+0x7f
fffff880`035d77e0 fffff880`010b895e fltmgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x24f
fffff880`035d7870 fffff800`041b9f97 fltmgr!FltpFsControl+0xee,
蹦处的代码:
*(_QWORD *)(v30 + 56) = MrxDAVEfsControlCompletion;
抽取关键代码就是:
v6 = *(_QWORD *)(a1 + 0x58);
v7 = *(_QWORD *)(v6 + 32);
DeviceObject = *(PDEVICE_OBJECT *)(v7 + 32);
LOBYTE(v23) = DeviceObject->StackSize;
LODWORD(v24) = RxCeAllocateIrpWithMDL_0(v23, 0i64, 0i64);
v3 = v24;
v29 = *(_QWORD *)(v3 + 184);
v30 = *(_QWORD *)(v3 + 184) - 72i64;
*(_QWORD *)(v30 + 56) = MrxDAVEfsControlCompletion;用windbg看可以看出:
LOBYTE(v23) = DeviceObject->StackSize;结果是255
因为人家给的是win7 32位的exploit,放在win7 64只能蓝屏,对照win7 32位的可以看出是0x30处有个10,也就是StackSize字段是10,但是在64位的系统偏移是在4c处,后面的
“ v2 = IofCallDriver(DeviceObject, (PIRP)v3); ” 就是提权的利用代码了。
我直接用汇编拼64位系统上可以运行的exploit拼了好久,拼不出来,最后卡在kernel32的这个dll的GetCurrentProcessId上面,一跑到这个函数就崩溃,内核里面的指令集是不兼容的,我不知道怎么处理了。
附上我的拼的结果吧,最后可以进shellcode函数,但是shellcode不兼容。那个shellcode_17.dll名字要改成shellcode.dll,我是直接在16进制编辑器上面改的,也附上源作者的eop吧。
调试时候,直接在bp mrxdav!MRxDAVDevFcbXXXControlFile,MRxDAVCreateContinuation,MrxDAVEfsControl三个函数上面下断,找到rx_context结构体,然后就在pRelevantSrvOpen这个指针上面下写入断点,在到它的设备链表上面下断点,就行了,就可以找到做手脚的地方,shellcode做的假的device_object是放在00地址处,这个结构体有指向shellcode的提权函数指针,64位是在d8处指向提权代码,我的exp,shellcode不兼容,可以进提权函数,但是提权函数会在内核里面崩溃,源作者的在32位里面可以完美提权。
