Talos Vulnerability Report

TALOS-2020-1033

Windows 10 Insider Preview Fast win32kbase HMMarkObjectDestroy Arbitrary Code Execution Vulnerability Regression

May 5, 2020
CVE Number

Summary

A use after free vulnerability exists in Windows 10, Insider Preview Fast 10.0.19582.1001, when a Win32k component fails to properly handle objects in memory. Successful exploitation of this vulnerability can lead to arbitrary code execution in the kernel context and elevation of privileges.

Tested Versions

Microsoft Corporation Windows 10 Kernel Insider Preview Fast 10.0.19582.1001

Product URLs

https://www.microsoft.com/en-us/

CVSSv3 Score

8.4 - CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-416 - Use After Free

Details

With the lock down of user-space application capabilities, Windows kernel’s attack surface presents a popular target for local privilege escalation exploits.

The following vulnerability is a bug regression and incomplete patch of a vulnerability previously reported by Talos. Previously reported vulnerability was tracked as TALOS-2019-0970 and was assigned CVE-2020-0731 by Microsoft.

The original patch for this vulnerability can be bypassed using “SC_CLOSE” as argument to SendMessage instead of WM_CLOSE. The regression is currently only present in Insider Preview Fast version of Windows 10. Stable release is not affected.

Following analysis is updated to reflect the current version of Insider Fast.

Windows kernel crashes inside HMMarkObjectDestroy from win32kbase. Pseudocode of this function is as follows:

uint HMMarkObjectDestroy(ushort *param_1)
{
  byte bVar1;
  int iVar2;
  int iVar3;

  GetDomainLockRef(0xe);
  iVar3 = (uint)*param_1 * DAT_001c0b38 + DAT_001c0b34;
  bVar1 = *(byte *)(iVar3 + 0xd); // CRASH HERE
  return (uint)(iVar2 == 0);
}

Based on the above code, variable bVar1 is being assigned a value from a pointer created based on argument passed to function. This is more clearly visible from the context of the crash:

win32kbase!HMMarkObjectDestroy+0x22:
96173874 8a510d          mov     dl,byte ptr [ecx+0Dh]      ds:0023:9537bf8d=??

c346380c 96b7d886 039111d8 b8fd2d00 c3463870 win32kbase!HMMarkObjectDestroy+0x22
c346381c 96bce02e 039111d8 ab049390 93a305b0 win32kfull!_DestroyMenu+0x18
c3463870 96bcf571 00000000 952e33a0 954026b8 win32kfull!xxxFreeWindow+0xa80
c34638cc 96169fc8 b8fd2d00 000026b8 0000033a win32kfull!xxxDestroyWindow+0x28f
c34638f0 9616a298 c00000bb 00000000 ab049390 win32kbase!HMDestroyUnlockedObjectWorker+0x42
c3463918 9616f3b4 1817dd45 00000000 00000000 win32kbase!DestroyThreadsObjects+0xf8
c3463a08 96171ef7 00000001 00000000 93db4040 win32kbase!xxxDestroyThreadInfo+0x490
c3463a3c 96b8655e 93db4040 00000001 c3463b3c win32kbase!UserThreadCallout+0x311

We can observe a direct control over this pointer However when the 4th parameter of SetWindowLongPtrW function in POC will be change from The value of this pointer comes directly from the 3rd parameter of a call to SetWindowLongPtrW:

LONG_PTR zmienna00422 = SetWindowLongPtrW(zmienna00185, GWL_ID, zmienna00222);

We can actually directly control the pointer value, like so:

LONG_PTR zmienna00422 = SetWindowLongPtrW(zmienna00185, GWL_ID, 0x41414141);

MSDN definition for SetWindowLongPtr function states: “Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory. “

When directly controlling this pointer value, a crash happens on a different instruction:

win32kfull!UnlockWndMenuWorker+0x20:
9693f48e 3b31            cmp     esi,dword ptr [ecx]  ds:0023:41414175=????????

9f2f7820 9690e020 c0b4a7d8 95e305b0 bf5bbc30 win32kfull!UnlockWndMenuWorker+0x20
9f2f7870 9690f571 00000000 960e4160 96203108 win32kfull!xxxFreeWindow+0xa72
9f2f78cc 95a29fc8 bf5bbc30 00003108 00000416 win32kfull!xxxDestroyWindow+0x28f
9f2f78f0 95a2a298 c00000bb 00000000 c0b4a7d8 win32kbase!HMDestroyUnlockedObjectWorker+0x42
9f2f7918 95a2f3b4 957371ca 00000000 00000000 win32kbase!DestroyThreadsObjects+0xf8
9f2f7a08 95a31ef7 00000001 00000000 c5a10040 win32kbase!xxxDestroyThreadInfo+0x490
9f2f7a3c 968c655e c5a10040 00000001 9f2f7b3c win32kbase!UserThreadCallout+0x311

To reach the state at which this vulnerability can be triggered we need a couple of steps: 1. Create new UWP Metro process/application 2. Find HWND to the process, which is ApplicationFrameWindow Class 3. Create new HWND with a syscall to NtUserRealChildWindowFromPoint, which returns Windows.UI.Core.CoreWindow Class 4. Change attribute of HWND created in step 3 thanks to functions SetWindowLongPtrA and SetWindowLongPtrW. 5. Close the opened process in step 1 by SendMessage with VM_CLOSE as parameter, this frees all HWNDs associated with process

In step 3 there is call to the syscall NtUserRealChildWindowFromPoint which produces new HWND handle to child window of process that is created in step 1. Reverse engineering this syscall reveals that is actually has 2 parameters instead of 3 which results in the following pseudocode:

HWND NtUserRealChildWindowFromPoint(HWND param_1,POINT param_2)
{
  HANDLE this;
  HWND *ppHVar1;
  HWND pHVar2;
  
  pHVar2 = (HWND)0x0;
  EnterSharedCrit(0,1);
  this = ValidateHwnd();
  if (this != (HANDLE)0x0) {
    ppHVar1 = (HWND *)__RealChildWindowFromPoint@12((LONG_PTR)this,param_2.x,param_2.y);
    if (ppHVar1 != (HWND *)0x0) {
      pHVar2 = *ppHVar1;
    }
  }
  UserSessionSwitchLeaveCrit();
  return pHVar2;
}

Supplying the correct arguments to the above syscall actually produces a HWND handle. The hint for the valid arguments is based on __RealChildWindowFromPoint function. Using POINT structure, rather than two separate ints (int param_2, int param_3) works correctly.

In this case NtUserRealChildWindowFromPoint returns HWDN to Windows.UI.Core.CoreWindow Class

Within step 5 the actual HWND window of process closes and in result the HWND for whole Metro program are freed. Everything happens within function _DestroyMenu :

BOOL _DestroyMenu(int hMenu)
{
  undefined4 *puVar1;
  int iVar2;
  int iVar3;
  BOOL BVar4;
  int *piVar5;
  
  iVar2 = hMenu;
  if (hMenu == 0) {
    BVar4 = 0;
  }
  else {
    iVar3 = HMMarkObjectDestroy(hMenu); // _DestroyMenu+0x18
    if (iVar3 != 0) {
      piVar5 = *(int **)(iVar2 + 0x38);
      iVar3 = *(int *)(*(int *)(iVar2 + 0x14) + 0x18);
      if (iVar3 != 0) {
        do {
          _MNFreeItem@12(iVar2,piVar5,1);
          piVar5 = piVar5 + 0x14;
          iVar3 = iVar3 + -1;
        } while (iVar3 != 0);
        piVar5 = *(int **)(iVar2 + 0x38);
      }
      if (piVar5 != (int *)0x0) {
        RtlFreeHeap(*(undefined4 *)(*(int *)(iVar2 + 0xc) + 0x40),0,*(undefined4 *)(iVar2 + 0x3c));
        Win32FreePool(*(undefined4 *)(iVar2 + 0x38));
      }
      hMenu = 0;
    [...............]
}

However, at this point the function SetWindowLongPtrW still holds value to one of the HWND handle’s properties of window which are freed in step 5. This can be reused which leads to memory corruption and can ultimately be abused to achieve arbitrary code execution in the kernel context.

Crash Information


*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

KMODE_EXCEPTION_NOT_HANDLED (1e)
This is a very common bugcheck.  Usually the exception address pinpoints
the driver/function that caused the problem.  Always note this address
as well as the link date of the driver/image that contains this address.
Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: 9ac1c063, The address that the exception occurred at
Arg3: 00000000, Parameter 0 of the exception
Arg4: 99bb470d, Parameter 1 of the exception

Debugging Details:
------------------

Page 1ac95 not present in the dump file. Type ".hh dbgerr004" for details

KEY_VALUES_STRING: 1


PROCESSES_ANALYSIS: 1

SERVICE_ANALYSIS: 1

STACKHASH_ANALYSIS: 1

TIMELINE_ANALYSIS: 1


DUMP_CLASS: 1

DUMP_QUALIFIER: 401

BUILD_VERSION_STRING:  19582.1001.x86fre.rs_prerelease.200306-1640

SYSTEM_MANUFACTURER:  Microsoft Corporation

VIRTUAL_MACHINE:  HyperV

SYSTEM_PRODUCT_NAME:  Virtual Machine

SYSTEM_VERSION:  7.0

BIOS_VENDOR:  American Megatrends Inc.

BIOS_VERSION:  090007 

BIOS_DATE:  05/18/2018

BASEBOARD_MANUFACTURER:  Microsoft Corporation

BASEBOARD_PRODUCT:  Virtual Machine

BASEBOARD_VERSION:  7.0

DUMP_TYPE:  1

BUGCHECK_P1: ffffffffc0000005

BUGCHECK_P2: ffffffff9ac1c063

BUGCHECK_P3: 0

BUGCHECK_P4: ffffffff99bb470d

READ_ADDRESS: Unable to locate session special pool (nt!MiSessionSpecialPool)
 99bb470d Paged session pool

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

FAULTING_IP: 
win32kbase!HMMarkObjectDestroy+1b
9ac1c063 8a510d          mov     dl,byte ptr [ecx+0Dh]

EXCEPTION_PARAMETER2:  99bb470d

BUGCHECK_STR:  0x1E_c0000005_R

CPU_COUNT: 1

CPU_MHZ: e70

CPU_VENDOR:  GenuineIntel

CPU_FAMILY: 6

CPU_MODEL: 55

CPU_STEPPING: 4

CPU_MICROCODE: 6,55,4,0 (F,M,S,R)  SIG: FFFFFFFF'00000000 (cache) FFFFFFFF'00000000 (init)

BLACKBOXBSD: 1 (!blackboxbsd)


BLACKBOXNTFS: 1 (!blackboxntfs)


BLACKBOXWINLOGON: 1

DEFAULT_BUCKET_ID:  WIN8_DRIVER_FAULT

PROCESS_NAME:  Calculator.exe

CURRENT_IRQL:  0

ANALYSIS_SESSION_HOST:  DESKTOP-NL01NBG

ANALYSIS_SESSION_TIME:  03-17-2020 09:23:55.0377

ANALYSIS_VERSION: 10.0.18362.1 x86fre

EXCEPTION_RECORD:  a77d9fd8 -- (.exr 0xffffffffa77d9fd8)
ExceptionAddress: 9ac1c063 (win32kbase!HMMarkObjectDestroy+0x0000001b)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 99bb470d
Attempt to read from address 99bb470d

TRAP_FRAME:  a77da0b4 -- (.trap 0xffffffffa77da0b4)
ErrCode = 00000000
eax=02f23a18 ebx=b8692a90 ecx=99bb4700 edx=9adb79a0 esi=00000000 edi=9ac618c6
eip=9ac1c063 esp=a77da128 ebp=a77da12c iopl=0         nv up ei ng nz na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010286
win32kbase!HMMarkObjectDestroy+0x1b:
9ac1c063 8a510d          mov     dl,byte ptr [ecx+0Dh]      ds:0023:99bb470d=??
Resetting default scope

LAST_CONTROL_TRANSFER:  from 84e3f6f2 to 84d8220c

STACK_TEXT:  
a77d9a78 84e3f6f2 0000001e c0000005 9ac1c063 nt!KeBugCheckEx
a77d9a94 84daf302 a77d9fd8 84f4a570 a77d9b90 nt!KiFatalExceptionHandler+0x1a
a77d9ab8 84daf2d4 a77d9fd8 84f4a570 a77d9b90 nt!ExecuteHandler2+0x26
a77d9b80 84d392d5 a77d9fd8 a77d9b90 00010037 nt!ExecuteHandler+0x24
a77d9fbc 84da7f31 a77d9fd8 00000000 a77da0b4 nt!KiDispatchException+0x1b5
a77da028 84dac812 00000000 00000000 00000000 nt!KiDispatchTrapException+0x51
a77da028 9ac1c063 00000000 00000000 00000000 nt!KiTrap0E+0x382
a77da12c 9c183147 02f23a18 9ac618c6 02f23a18 win32kbase!HMMarkObjectDestroy+0x1b
a77da154 9c17b90b 02f23a18 ae229c98 998305b0 win32kfull!_DestroyMenu+0x2d
a77da1a8 9c17aece 00000000 99e01b48 99ae2460 win32kfull!xxxFreeWindow+0x80b
a77da204 9ac5cab6 b8692a90 00001b48 00000246 win32kfull!xxxDestroyWindow+0x758
a77da238 9ac5ca36 00000000 00000000 ae229c98 win32kbase!HMDestroyUnlockedObjectWorker+0x64
a77da260 9ac5e9f7 af23f160 00000000 ae229c98 win32kbase!DestroyThreadsObjects+0xf6
a77da34c 9acf8109 00000001 00000000 b7a126c0 win32kbase!xxxDestroyThreadInfo+0x497
a77da384 9c17db82 b7a126c0 00000001 a77da47c win32kbase!UserThreadCallout+0x14f
a77da3a0 9ac5a9d9 b7a126c0 00000001 a77da47c win32kfull!W32pThreadCallout+0x50
a77da3d4 9aeb1093 00000000 00000001 a77da47c win32kbase!W32CalloutDispatch+0xc9
a77da3f4 8507d8f5 00000000 00000001 a77da47c win32k!W32CalloutDispatchThunk+0x23
a77da414 8507d87e a77da47c a362c800 00000000 nt!ExCallCallBack+0x21
a77da428 8507cfa3 00000001 a77da47c 00000000 nt!PsInvokeWin32Callout+0x28
a77da4c8 85081040 00000000 00000000 00000280 nt!PspExitThread+0x36d
a77da4dc 84cb8ade b7a12850 a77da50c a77da518 nt!KiSchedulerApcTerminate+0x38
a77da53c 84da66e4 00000001 00000000 a77da554 nt!KiDeliverApc+0x34e
a77da53c 77b67aa0 00000001 00000000 a77da554 nt!KiServiceExit+0x76
WARNING: Frame IP not in any known module. Following frames may be wrong.
02c1f414 00000000 00000000 00000000 00000000 0x77b67aa0


THREAD_SHA1_HASH_MOD_FUNC:  e389d5d9ff99c42a05d50d72121d7248ef311c77

THREAD_SHA1_HASH_MOD_FUNC_OFFSET:  08e6ecde98f2d92d9941548a38195d7f72480f60

THREAD_SHA1_HASH_MOD:  1cc54735fffd0313042a9faec840a19a44322d7d

FOLLOWUP_IP: 
win32kbase!HMMarkObjectDestroy+1b
9ac1c063 8a510d          mov     dl,byte ptr [ecx+0Dh]

FAULT_INSTR_CODE:  800d518a

SYMBOL_STACK_INDEX:  7

SYMBOL_NAME:  win32kbase!HMMarkObjectDestroy+1b

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: win32kbase

IMAGE_NAME:  win32kbase.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  0

STACK_COMMAND:  .thread ; .cxr ; kb

BUCKET_ID_FUNC_OFFSET:  1b

FAILURE_BUCKET_ID:  0x1E_c0000005_R_VRF_win32kbase!HMMarkObjectDestroy

BUCKET_ID:  0x1E_c0000005_R_VRF_win32kbase!HMMarkObjectDestroy

PRIMARY_PROBLEM_CLASS:  0x1E_c0000005_R_VRF_win32kbase!HMMarkObjectDestroy

TARGET_TIME:  2020-03-17T15:32:34.000Z

OSBUILD:  19582

OSSERVICEPACK:  0

SERVICEPACK_NUMBER: 0

OS_REVISION: 0

SUITE_MASK:  272

PRODUCT_TYPE:  1

OSPLATFORM_TYPE:  x86

OSNAME:  Windows 10

OSEDITION:  Windows 10 WinNt TerminalServer SingleUserTS

OS_LOCALE:  

USER_LCID:  0

OSBUILD_TIMESTAMP:  unknown_date

BUILDDATESTAMP_STR:  200306-1640

BUILDLAB_STR:  rs_prerelease

BUILDOSVER_STR:  10.0.19582.1001.x86fre.rs_prerelease.200306-1640

ANALYSIS_SESSION_ELAPSED_TIME:  74d3

ANALYSIS_SOURCE:  KM

FAILURE_ID_HASH_STRING:  km:0x1e_c0000005_r_vrf_win32kbase!hmmarkobjectdestroy

FAILURE_ID_HASH:  {73139ba2-d027-0649-4487-33e4cda1d19b}

Followup:     MachineOwner
---------

Exploit Proof of Concept

The proof of concept attached to this advisory can be built with the following:

cl.exe /Zi /w poc.cpp gdi32.lib kernel32.lib User32.lib Advapi32.lib Shell32.lib Msimg32.lib Dxva2.lib Mscms.lib

Timeline

2020-03-20 - Vendor Disclosure

2020-05-05 - Public Release

Credit

Discovered by Marcin Towalski of Cisco Talos.