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个主要函数:

  1. DriverEntry:驱动程序入口函数
  2. IrpDeviceIoCtlHandler:设备操作处理函数,处理DeviceIoControl请求
  3. DriverUnloadHandler:驱动卸载处理函数
  4. IrpCreateCloseHandler:驱动设备打开关闭处理函数
  5. IrpNotImplementedHandler:未实现功能处理

IRP请求通过驱动对象的MajorFunction属性绑定派遣函数:

  • IRP_MJ_CREATE对应CreateFile请求
  • IRP_MJ_CLOSE对应CloseHandle请求
  • IRP_MJ_DEVICE_CONTROL对应DeviceIoControl请求

1.2 UAF漏洞分析

1.2.1 UAF漏洞原理

UAF漏洞利用流程:

  1. 申请堆块并保存指针
  2. 释放堆块但不置空指针(形成悬挂指针)
  3. 再次申请堆块并填充恶意数据
  4. 使用悬挂指针实现恶意目的

内存释放后的三种情况:

  1. 指针被置NULL,程序崩溃
  2. 指针未置NULL且内存未被修改,程序正常运行
  3. 指针未置NULL且内存被修改,出现异常行为

1.2.2 HEVD中的UAF实现

漏洞代码位于UseAfterFreeNonPagedPool.c,包含4个关键函数:

  1. AllocateUaFObjectNonPagedPool

    • 使用ExAllocatePoolWithTag在内核非分页池申请内存
    • 填充_USE_AFTER_FREE_NON_PAGED_POOL结构体(大小0x58)
    • 将对象指针保存在全局变量g_UseAfterFreeObjectNonPagedPool
  2. FreeUaFObjectNonPagedPool

    • 释放内核对象
    • 修复版本会额外将指针置NULL
  3. AllocateFakeObjectNonPagedPool

    • 将用户模式数据拷贝到内核对象
    • 使用相同大小的FAKE_OBJECT_NON_PAGED_POOL结构体
  4. UseUaFObjectNonPagedPool

    • 调用g_UseAfterFreeObjectNonPagedPool的回调函数

1.2.3 漏洞触发流程

  1. 调用AllocateUaFObjectNonPagedPool创建对象
  2. 调用FreeUaFObjectNonPagedPool释放对象(不置NULL)
  3. 调用AllocateFakeObjectNonPagedPool覆盖释放的内存
  4. 调用UseUaFObjectNonPagedPool触发恶意代码

0x02 漏洞利用

2.1 池喷射技术

目的:提高申请到相同内存块的几率

步骤

  1. 申请10000个IoCompletionReserve对象(大小0x60)
  2. 再申请5000个相同对象
  3. 每隔一个释放一个对象,形成间隔空闲池块

原理

  • 优先使用大小相同或相近的池块
  • 后释放的池块优先被使用(LIFO)
  • 间隔释放防止池块合并

2.2 UAF利用代码

  1. 申请UAF_OBJECT结构体
  2. 释放UAF_OBJECT结构体
  3. 循环1000次申请FAKE_OBJECT结构体
  4. 使用悬挂指针触发恶意代码

2.3 Payload实现

目标:将SYSTEM进程token复制到当前进程

关键步骤

  1. 通过fs:[124h]获取当前线程_KTHREAD
  2. 通过_KTHREAD获取_EPROCESS
  3. 遍历进程活动链表(ActiveProcessLinks)查找PID=4的SYSTEM进程
  4. 复制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 查询对象类型

  1. 使用!object \ObjectTypes获取对象列表
  2. 使用dt nt!_OBJECT_TYPE查看对象详细信息
  3. 关键字段:
    • PoolType:对象分配的内存池类型
    • DefaultNonPagedPoolCharge:非分页池大小

4.2 实际对象大小分析

  1. 创建对象并获取句柄
  2. 使用!handle命令查找对象地址
  3. 使用!pool命令查看池块信息

0x05 防御措施

  1. 释放内存后立即将指针置NULL
  2. 使用安全的内存管理函数
  3. 启用池内存保护机制(如PoolCanary)
  4. 实施严格的输入验证

0x06 参考资源

  1. Windows内核池喷射技术
  2. Windows堆喷射技术
  3. 内核池原理
  4. BlackHat 2011内核池论文
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 3.2 ExAllocatePoolWithTag 3.3 ProbeForRead 3.4 NtAllocateReserveObject 支持两种对象类型: 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) 实施严格的输入验证 0x06 参考资源 Windows内核池喷射技术 Windows堆喷射技术 内核池原理 BlackHat 2011内核池论文