Talos Vulnerability Report

TALOS-2019-0970

Windows 10 win32kbase HMMarkObjectDestroy Arbitrary Code Execution Vulnerability

February 11, 2020
CVE Number

CVE-2020-0731

Summary

A use after free vulnerability exists in Windows 10, Version 10.0.19033.1, 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. This vulnerability occurs only on x86 machine.

Tested Versions

Microsoft Windows Version 10.0.19033.1 Insider Preview Fast x86

Product URLs

https://www.microsoft.com/

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.

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 param2, int param3) 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.

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

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: 96173874, The address that the exception occurred at
Arg3: 00000000, Parameter 0 of the exception
Arg4: 9537bf8d, Parameter 1 of the exception

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


KEY_VALUES_STRING: 1

    Key  : Analysis.CPU.Sec
    Value: 4

    Key  : Analysis.DebugAnalysisProvider.CPP
    Value: Create: 8007007e on DESKTOP-K7AUTAO

    Key  : Analysis.DebugData
    Value: CreateObject

    Key  : Analysis.DebugModel
    Value: CreateObject

    Key  : Analysis.Elapsed.Sec
    Value: 4

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 132

    Key  : Analysis.System
    Value: CreateObject


BUGCHECK_CODE:  1e

BUGCHECK_P1: ffffffffc0000005

BUGCHECK_P2: ffffffff96173874

BUGCHECK_P3: 0

BUGCHECK_P4: ffffffff9537bf8d

READ_ADDRESS:  9537bf8d Paged session pool

EXCEPTION_PARAMETER2:  9537bf8d

PROCESS_NAME:  Calculator.exe

TRAP_FRAME:  c3463794 -- (.trap 0xffffffffc3463794)
ErrCode = 00000000
eax=039111d8 ebx=00000000 ecx=9537bf80 edx=ab049500 esi=00000000 edi=039111d8
eip=96173874 esp=c3463808 ebp=c346380c iopl=0         nv up ei ng nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010382
win32kbase!HMMarkObjectDestroy+0x22:
96173874 8a510d          mov     dl,byte ptr [ecx+0Dh]      ds:0023:9537bf8d=??
Resetting default scope

STACK_TEXT:  
c3462cdc 85a1e4f9 00000003 099ba98b 00000065 nt!RtlpBreakWithStatusInstruction
c3462d30 85a1dec5 8528d340 c3463120 c3463154 nt!KiBugCheckDebugBreak+0x1f
c34630f4 85973c02 0000001e c0000005 96173874 nt!KeBugCheck2+0x79d
c3463118 85973b39 0000001e c0000005 96173874 nt!KiBugCheck2+0xc6
c3463138 85a1a3ca 0000001e c0000005 96173874 nt!KeBugCheckEx+0x19
c3463154 8598e2b2 c34636b8 85b135a8 c3463260 nt!KiFatalExceptionHandler+0x1a
c3463178 8598e284 c34636b8 85b135a8 c3463260 nt!ExecuteHandler2+0x26
c3463250 858b01b8 c34636b8 c3463260 00010037 nt!ExecuteHandler+0x24
c346369c 85986f21 c34636b8 00000000 c3463794 nt!KiDispatchException+0x2b8
c3463708 8598b802 00000000 00000000 00000000 nt!KiDispatchTrapException+0x51
c3463708 96173874 00000000 00000000 00000000 nt!KiTrap0E+0x382
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
c3463a58 9617525c 93db4040 00000001 c000001c win32kfull!W32pThreadCallout+0x50
c3463a94 9402108b 00000000 00000001 c3463b3c win32kbase!W32CalloutDispatch+0xcc
c3463ab4 85bd753b 00000000 00000001 c3463b3c win32k!W32CalloutDispatchThunk+0x23
c3463ad4 85bd74ca c3463b3c acd71680 00000000 nt!ExCallCallBack+0x21
c3463ae8 85bdb03f 00000001 c3463b3c 00000000 nt!PsInvokeWin32Callout+0x28
c3463b88 85bdffce 00000000 00000000 00000000 nt!PspExitThread+0x36d
c3463b9c 8585771e 93db41d0 c3463bcc c3463bd8 nt!KiSchedulerApcTerminate+0x38
c3463bfc 859856d4 00000001 00000000 c3463c14 nt!KiDeliverApc+0x34e
c3463bfc 770613a0 00000001 00000000 c3463c14 nt!KiServiceExit+0x76
WARNING: Stack unwind information not available. Following frames may be wrong.
02e3f3c8 7058d960 012a0550 7069d364 00000000 ntdll!KiFastSystemCallRet
02e3f3ec 7058d701 012a0540 07a95940 02e3f458 twinapi_appcore!PsmWaitForAppResume+0x30
02e3f424 7058dc06 0124f5f0 0000028c 012a0540 twinapi_appcore!Ordinal8+0xa7f1
02e3f524 6d81786e 07a95940 dcb09a44 01303b20 twinapi_appcore!PsmUnblockAppStateChangeCompletion+0x256
02e3f594 6d816be9 02e3f5f7 00000000 01303aa8 Windows_UI!Ordinal1700+0xd28e
02e3f5d4 6d81693d 00000001 02e3f5f6 02e3f5f7 Windows_UI!Ordinal1700+0xc609
02e3f658 6d826f95 00000000 00000000 6d826f30 Windows_UI!Ordinal1700+0xc35d
02e3f6c4 6cae506c 01303aa8 00000002 01226030 Windows_UI!DllGetActivationFactory+0x24f5
02e3f70c 705770c2 0125e070 01226030 02e3f74c Windows_UI_Xaml+0x31506c
02e3f71c 70577c3b 0124f5f0 a717ecb6 00000000 twinapi_appcore!BiChangeApplicationStateForPackageNameForUser+0x4ca2
02e3f74c 76579100 01226030 76579040 76579040 twinapi_appcore!BiChangeApplicationStateForPackageNameForUser+0x581b
02e3f7d0 76a0cec9 00c7f838 76a0ceb0 02e3f83c shcore!Ordinal144+0x5f0
02e3f7e0 77035e8d 00c7f838 8c87f078 00000000 KERNEL32!BaseThreadInitThunk+0x19
02e3f83c 77035e61 ffffffff 77073a55 00000000 ntdll!EtwProcessPrivateLoggerRequest+0xd8d
02e3f850 76579040 00c7f838 00000000 00000000 ntdll!EtwProcessPrivateLoggerRequest+0xd61
00000000 00000000 00000000 00000000 00000000 shcore!Ordinal144+0x530


SYMBOL_NAME:  win32kbase!HMMarkObjectDestroy+22

MODULE_NAME: win32kbase

IMAGE_NAME:  win32kbase.sys

STACK_COMMAND:  .thread ; .cxr ; kb

BUCKET_ID_FUNC_OFFSET:  22

FAILURE_BUCKET_ID:  0x1E_c0000005_R_win32kbase!HMMarkObjectDestroy

OS_VERSION:  10.0.19033.1

BUILDLAB_STR:  vb_release

OSPLATFORM_TYPE:  x86

OSNAME:  Windows 10

FAILURE_ID_HASH:  {fa38f266-a2d1-14c5-d08c-7ec0b492a0c2}

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

Timeline

2019-12-03 - Vendor Disclosure
2020-02-10 - Public Release

Credit

Discovered by Marcin Towalski of Cisco Talos.