漏洞描述
内核模块win32kfull.sys的win32kfull!xxxClientAllocWindowClassExtraBytes函数中存在Type Confusion漏洞,利用此漏洞进行越界读写,最终可实现本地提权
官方通报影响的windows版本:
Windows 10 Version 1803/1809/1909/2004/20h2
Windows Server, version 1909/20H2(Server Core installation)
Windows 10 Version for 32-bit Systems
Windows Server 2019
漏洞分析
分析Windows版本:win10 20h2 19042.508
Type Confusion漏洞存在于win32kfull!xxxCreateWindowEx函数中,函数中漏洞点的伪代码如下:
漏洞是怎么出现的呢?这得从窗口创建说起
【→所有资源关注我,私信回复“资料”获取←】
1、网络安全学习路线
2、电子书籍(白帽子)
3、安全大厂内部视频
4、100份src文档
5、常见安全面试题
6、ctf大赛经典题目解析
7、全套工具包
8、应急响应笔记
创建一个自定义的窗口前需要注册自定义的窗口类,窗口类的结构体如下:
typedef struct tagWNDCLASSA {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;
填写好窗口类的结构体的成员,紧接着就可以调用CreateWindow(EXA/W)创建窗口,R0到R3的执行总体流程如下:
00 fffffe82`32d3f848 fffff467`52aa51a9 win32kfull!xxxCreateWindowEx
01 fffffe82`32d3f850 fffff467`5285519e win32kfull!NtUserCreateWindowEx+0x679
02 fffffe82`32d3f9f0 fffff802`36e058b5 win32k!NtUserCreateWindowEx+0xc2
03 fffffe82`32d3fa90 00007ffe`d86e1ec4 nt!KiSystemServiceCopyEnd+0x25
04 00000062`2ad9f7d8 00007ffe`d8ca7d8b win32u!NtUserCreateWindowEx+0x14
05 00000062`2ad9f7e0 00007ffe`d8ca7958 USER32!VerNtUserCreateWindowEx+0x20f
06 00000062`2ad9fb70 00007ffe`d8ca3c92 USER32!CreateWindowInternal+0x1a4
07 00000062`2ad9fcd0 00007ff7`9418144d USER32!CreateWindowExA+0x82
可以看到创建窗口的时候最终会进入漏洞存在的函数win32kfull!xxxCreateWindowEx,那么怎样才能在win32kfull!xxxCreateWindowEx内调用win32kfull!xxxClientAllocWindowClassExtraBytes(即到达上图中line: 974)呢?
当tagWNDCLASSA类设置cbWndExtra成员(为窗口实例分配的额外的字节大小)不为0时,就会调用到win32kfull!xxxClientAllocWindowClassExtraBytes函数,问题就出在这个函数中
v50是一个tagWND结构体指针,tagWND在win10的版本中相比win7的版本发生了一些变化,tagWND结构体的关键成员如下(图片来源于红雨滴团队),(_QWORD )(((_QWORD *)v50 + 5) + 0x128i64)即为下图的pExtraBytes,在当前正常的执行流程中,赋值为win32kfull!xxxClientAllocWindowClassExtraBytes申请到的堆地址,怎么知道是堆地址呢?且看下文
对函数win32kfull!xxxClientAllocWindowClassExtraBytes进行反编译,得到以下结果:
volatile void *__fastcall xxxClientAllocWindowClassExtraBytes(SIZE_T Length)
{
SIZE_T v1; // rdi
int v2; // ebx
__int64 *v3; // rcx
volatile void *v4; // rbx
__int64 CurrentProcessWow64Process; // rax
unsigned __int64 v7; // [rsp+30h] [rbp-38h] BYREF
volatile void *v8; // [rsp+38h] [rbp-30h]
char v9; // [rsp+70h] [rbp+8h] BYREF
char v10; // [rsp+78h] [rbp+10h] BYREF
int v11; // [rsp+80h] [rbp+18h] BYREF
int v12; // [rsp+88h] [rbp+20h] BYREF
v1 = (unsigned int)Length;
v7 = 0i64;
v11 = 0;
v8 = 0i64;
v12 = Length;
if ( gdwInAtomicOperation && (gdwExtraInstrumentations & 1) != 0 )
KeBugCheckEx(0x160u, gdwInAtomicOperation, 0i64, 0i64, 0i64);
ReleaseAndReacquirePerObjectLocks::ReleaseAndReacquirePerObjectLocks((ReleaseAndReacquirePerObjectLocks *)&v10);
LeaveEnterCritProperDisposition::LeaveEnterCritProperDisposition((LeaveEnterCritProperDisposition *)&v9);
EtwTraceBeginCallback(0x7Bi64);
v2 = KeUserModeCallback(0x7Bi64, &v12, 4i64, &v7, &v11);
EtwTraceEndCallback(0x7Bi64);
LeaveEnterCritProperDisposition::~LeaveEnterCritProperDisposition((LeaveEnterCritProperDisposition *)&v9);
ReleaseAndReacquirePerObjectLocks::~ReleaseAndReacquirePerObjectLocks((ReleaseAndReacquirePerObjectLocks *)&v10);
if ( v2 < 0 || v11 != 0x18 )
return 0i64;
v3 = (__int64 *)v7;
if ( v7 + 8 < v7 || v7 + 8 > MmUserProbeAddress )
v3 = (__int64 *)MmUserProbeAddress;
v8 = (volatile void *)*v3;
v4 = v8;
CurrentProcessWow64Process = PsGetC