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.
Microsoft Corporation Windows 10 Kernel Insider Preview Fast 10.0.19582.1001
https://www.microsoft.com/en-us/
8.4 - CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE-416 - Use After Free
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.
* *
* 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
---------
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
2020-03-20 - Vendor Disclosure
2020-05-05 - Public Release
Discovered by Marcin Towalski of Cisco Talos.
This vulnerability has not been disclosed and cannot be viewed at this time.