windows内核漏洞分析-01 UAF
字数 2217 2025-08-24 07:48:22
Windows内核漏洞分析与利用:UAF漏洞详解
0x00 前言
本文详细分析Windows内核中的Use-After-Free (UAF)漏洞原理、利用方法及防御措施,基于HEVD (HackSys Extreme Vulnerable Driver)项目进行讲解。
0x01 HEVD代码分析
1.1 驱动程序逻辑
HEVD驱动程序主文件为HackSysExtremeVulnerableDriver.c,包含5个主要函数:
- DriverEntry:驱动程序入口函数
- IrpDeviceIoCtlHandler:设备操作处理函数,处理DeviceIoControl请求
- DriverUnloadHandler:驱动卸载处理函数
- IrpCreateCloseHandler:驱动设备打开关闭处理函数
- IrpNotImplementedHandler:未实现功能处理
IRP请求通过驱动对象的MajorFunction属性绑定派遣函数:
IRP_MJ_CREATE对应CreateFile请求IRP_MJ_CLOSE对应CloseHandle请求IRP_MJ_DEVICE_CONTROL对应DeviceIoControl请求
1.2 UAF漏洞分析
1.2.1 UAF漏洞原理
UAF漏洞利用流程:
- 申请堆块并保存指针
- 释放堆块但不置空指针(形成悬挂指针)
- 再次申请堆块并填充恶意数据
- 使用悬挂指针实现恶意目的
内存释放后的三种情况:
- 指针被置NULL,程序崩溃
- 指针未置NULL且内存未被修改,程序正常运行
- 指针未置NULL且内存被修改,出现异常行为
1.2.2 HEVD中的UAF实现
漏洞代码位于UseAfterFreeNonPagedPool.c,包含4个关键函数:
-
AllocateUaFObjectNonPagedPool
- 使用
ExAllocatePoolWithTag在内核非分页池申请内存 - 填充
_USE_AFTER_FREE_NON_PAGED_POOL结构体(大小0x58) - 将对象指针保存在全局变量
g_UseAfterFreeObjectNonPagedPool
- 使用
-
FreeUaFObjectNonPagedPool
- 释放内核对象
- 修复版本会额外将指针置NULL
-
AllocateFakeObjectNonPagedPool
- 将用户模式数据拷贝到内核对象
- 使用相同大小的
FAKE_OBJECT_NON_PAGED_POOL结构体
-
UseUaFObjectNonPagedPool
- 调用
g_UseAfterFreeObjectNonPagedPool的回调函数
- 调用
1.2.3 漏洞触发流程
- 调用
AllocateUaFObjectNonPagedPool创建对象 - 调用
FreeUaFObjectNonPagedPool释放对象(不置NULL) - 调用
AllocateFakeObjectNonPagedPool覆盖释放的内存 - 调用
UseUaFObjectNonPagedPool触发恶意代码
0x02 漏洞利用
2.1 池喷射技术
目的:提高申请到相同内存块的几率
步骤:
- 申请10000个
IoCompletionReserve对象(大小0x60) - 再申请5000个相同对象
- 每隔一个释放一个对象,形成间隔空闲池块
原理:
- 优先使用大小相同或相近的池块
- 后释放的池块优先被使用(LIFO)
- 间隔释放防止池块合并
2.2 UAF利用代码
- 申请UAF_OBJECT结构体
- 释放UAF_OBJECT结构体
- 循环1000次申请FAKE_OBJECT结构体
- 使用悬挂指针触发恶意代码
2.3 Payload实现
目标:将SYSTEM进程token复制到当前进程
关键步骤:
- 通过
fs:[124h]获取当前线程_KTHREAD - 通过
_KTHREAD获取_EPROCESS - 遍历进程活动链表(
ActiveProcessLinks)查找PID=4的SYSTEM进程 - 复制SYSTEM的token(
_EPROCESS+0xF8)到当前进程
0x03 关键API分析
3.1 DeviceIoControl
BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice,
_In_ DWORD dwIoControlCode,
_In_opt_ LPVOID lpInBuffer,
_In_ DWORD nInBufferSize,
_Out_opt_ LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize,
_Out_opt_ LPDWORD lpBytesReturned,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
3.2 ExAllocatePoolWithTag
PVOID ExAllocatePoolWithTag(
POOL_TYPE PoolType,
SIZE_T NumberOfBytes,
ULONG Tag
);
3.3 ProbeForRead
void ProbeForRead(
const volatile VOID *Address,
SIZE_T Length,
ULONG Alignment
);
3.4 NtAllocateReserveObject
NTSTATUS STDCALL NtAllocateReserveObject(
OUT PHANDLE hObject,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN DWORD ObjectType
);
支持两种对象类型:
APC_OBJECT(0)IO_COMPLETION_OBJECT(1)
0x04 内核对象分析
4.1 查询对象类型
- 使用
!object \ObjectTypes获取对象列表 - 使用
dt nt!_OBJECT_TYPE查看对象详细信息 - 关键字段:
PoolType:对象分配的内存池类型DefaultNonPagedPoolCharge:非分页池大小
4.2 实际对象大小分析
- 创建对象并获取句柄
- 使用
!handle命令查找对象地址 - 使用
!pool命令查看池块信息
0x05 防御措施
- 释放内存后立即将指针置NULL
- 使用安全的内存管理函数
- 启用池内存保护机制(如PoolCanary)
- 实施严格的输入验证