Talos Vulnerability Report

TALOS-2019-0946

Microsoft Media Foundation IMFASFSplitter::Initialize Code Execution Vulnerability

February 11, 2020
CVE Number

CVE-2020-0738

Summary

An exploitable type confusion vulnerability exists in the mfasfsrcsnk.dll of Microsoft Media Foundation 10.0.18362.207. A specially crafted ASF file can cause type confusion, resulting in remote code execution. An attacker needs to provide a malformed file to the victim to trigger the vulnerability.

Tested Versions

Windows 10 - Media Foundation ASF Source and Sink DLL - 10.0.18362.207 (WinBuild.160101.0800) x86

Product URLs

https://docs.microsoft.com/en-us/windows/win32/medfound/microsoft-media-foundation-sdk

CVSSv3 Score

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

CWE

CWE-704: Incorrect Type Conversion or Cast

Details

This vulnerability is present in the Media Foundation MPEG4 dll which is part of the Microsoft Media Foundation framework. The Microsoft Media Foundation is a COM based multimedia framework available in Microsoft Windows since Windows Vista. It provides all sort of functionality related with audio/video operations. A specially crafted ASF file can lead to a type confusion vulnerability and could subequently lead to remote code execution.

To trigger the vulnerability we will use one of the SDK example applications available here : https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/mediafoundation/asfparser.

Our PoC file structure looks as follows:

                   offset:                                                        real size:

                             +-------------------------------------------------+
                             |                                                 |
                         0   |                 Header Object                   |  0x1E
                             |                                                 |
                             |                                                 |
                      0x1E   |           Extended Content Description          |  0x1CE
                             |                                                 |
                             |                                                 |
                             |                                                 |
                     0x1EC   |         Stream Bitrate Properties Object        |  0x26
                             |                                                 |
                             |                                                 |
                             |                                                 |
                     0x212   |             File Properties Object              |  0x68
                             |                                                 |
                             |                                                 |
                             |
                     0x27A   |            Header Extension Object              |  0x2E
                             |     +--------------------------------------+    |
                             |     |                                      |    |
                             |     |                                      |    |
                     0x2A8   |     |  Extended Stream Properties Object   |    |  0x108
                             |     |                                      |    |
                     0x3B0   |     |  Extended Stream Properties Object   |    |  0x108
                             |     |                                      |    |
                     0x4B8   |     |  Extended Stream Properties Object   |    |  0x108
                             |     |                                      |    |
                             |     |                                      |    |
                             |     +--------------------------------------+    |
                             |                                                 |
                             |                                                 |
                             +-------------------------------------------------+

As the ASF file format documentation states : https://go.microsoft.com/fwlink/p/?linkid=31334

To be valid, the Header Object must contain a File Properties Object, a Header Extension Object, and at least one Stream Properties Object.

Our malformed ASF PoC file does not contain the Stream Properties Object but still the pContentInfo->ParseHeader method at asfparser/ASFManager.cpp#L194 returns without error.

Looking inside the IMFASFContentInfo::ParseHeader method, we can notice that is because the second malformed Extended Stream Properties Object at offset 0x3B0 is treated as a Stream Properties Object :

Line 1  7B5AB2D0
Line 2  signed int __thiscall CASFStreamPropertiesObjectEx::ReadInternal(_DWORD *this, _DWORD *a2)
Line 3  {
Line 4      (...)
Line 5        v186 = 0;
Line 6        if ( (unsigned __int16)Dst <= 0u )
Line 7        {
Line 8  LABEL_50:
Line 9          if ( (unsigned __int64)(readObjectSize - v29->currentSize) < *(_QWORD *)&v29->gap4[28] )
Line 10         {
Line 11           v155 = *(_DWORD *)&v29->gap4[8];
Line 12           vulnObject = 0;
Line 13           retCode = (*(int (__thiscall **)(_DWORD, int, int *, int (__stdcall **)(void *)))(*(_DWORD *)v155
Line 14                                                                                           + 28))(// 0x7B5AE710 - ReadPropertiesObject
Line 15                       *(_DWORD *)(*(_DWORD *)v155 + 28),
Line 16                       v155,
Line 17                       ASF_Stream_Properties_Object,
Line 18                       &vulnObject);
Line 19           if ( retCode >= 0 )
Line 20           {
Line 21             v158 = (struct_v157 *)v183;
Line 22             v183->vulnObject = vulnObject;  

The crucial issue here seems to be the value of objectSize

                    value   offset  size
QWORD objectSize    B70h    3C0h    8h      

which equals amount of bytes left to the end of the file. A value bigger than file size 0xF20 - 0x3B0 causes a parser error, the same happens for a smaller value.

Further, the ASF Stream Properties object is added to the one of the IMFASFContentInfo class containers via CASFUnknownContainer::InsertCompletedObject method. That container already contains two Extended Stream Properties objects. When the IMFASFContentInfo::ParseHeader method ends successfully, the IMFASFContentInfo object is passed as an argument to the IMFASFSplitter::Initialize method at line asfparser/ASFManager.cpp#L244.

The vulnerable code is located inside the CASFPacketParser::CreateExtensionSystemsList method:

call stack:
CMFASFSplitter::Initialize
    CASFPacketParser::CASFPacketParser
        CASFPacketParser::CreateExtensionSystemsList


Line 1 unsigned int __thiscall CASFPacketParser::CreateExtensionSystemsList(int this, int a2)
Line 2 {
Line 3   (...)
Line 4   objectCount = 0;
Line 5   errorCode = (void *)(*(int (__thiscall **)(_DWORD, int, int *, unsigned int *))(*(_DWORD *)objSome
Line 6                                                                              + 12))(// 0x7b5ad1c0 - CASFUnknownContainer::GetObjectCount
Line 7                      *(_DWORD *)(*(_DWORD *)objSome + 12),
Line 8                      objSome,
Line 9                      &ASF_Extended_Stream_Properties_Object,
Line 10                     &objectCount);
Line 11
Line 12 if ( objectCount )
Line 13 {
Line 14   for (int i = 0 ; i <  objectCount; i++ )
Line 15   {
Line 16     errorCode = (void *)(*(int (__thiscall **)(_DWORD, int, int *, unsigned int, struct_vulnBuffer **))(*(_DWORD *)objSome + 16))(// getObject - 0x7b5a1a20
Line 17                           *(_DWORD *)(*(_DWORD *)objSome + 16),
Line 18                           objSome,
Line 19                           &ASF_Extended_Stream_Properties_Object,
Line 20                           v10,
Line 21                           &extendedStreamProperties);
Line 22     if ( (signed int)errorCode < 0 )
Line 23     {
Line 24         //Handle error
Line 25     }
Line 26     else
Line 27     {
Line 28       _extendedStreamProperties = extendedStreamProperties;
Line 29       if ( extendedStreamProperties )
Line 30       {
Line 31         errorCode = 0;
Line 32         streamNumber = extendedStreamProperties->streamNumber;
Line 33         extensionSystemCount = extendedStreamProperties->extensionSystemCount;// XXX : OOB read/access
Line 34         _errorCode = 0;
Line 35         _extensionSystemCount = extensionSystemCount;
Line 36         extensionSystemPayload = objectMalloc(8u);
Line 37         if ( extensionSystemPayload )
Line 38         {
Line 39           v14 = allocExtensionSystemsPayload(
Line 40                   (int)extensionSystemPayload,
Line 41                   _extensionSystemCount,
Line 42                   (signed int *)&_errorCode);
Line 43           errorCode = _errorCode;
Line 44         }
Line 45         else
Line 46         {
Line 47           v14 = 0;
Line 48         }
Line 49         v39 = v14;
Line 50         _errorCode = v14;
Line 51         if ( (signed int)errorCode < 0 )
Line 52         {
Line 53             //Handle error
Line 54         }
Line 55         else if ( v14 )
Line 56         {
Line 57           
Line 58           for ( index_1 = 0;index_1 < (unsigned __int16)_extensionSystemCount )
Line 59           {
Line 60             errorCode = (void *)sub_7B5A0F8B(_extendedStreamProperties, index_1, (int)&a3, (int)&a4, 0, 0);// <--------- b000m!
Line 61
Line 62 (...)           

As we can see in a above code, the objects which are supposed to be ASF_Extended_Stream_Properties_Object are pulled out from a container via method at line 16. In our case the variable objectCount is equal 3 and two first object seems to be valid ASF_Extended_Stream_Properties_Object objects:

7b6739e8 ff5610         call    dword ptr [esi+10h] // getObject
7b6739eb 8bf8           mov     edi, eax
7b6739ed 85ff           test    edi, edi
7b6739ef 0f882a030000   js      mfasfsrcsnk!MFCreateASFStreamingMediaSink+0x4571f (7b673d1f)
7b6739f5 8b75f4         mov     esi, dword ptr [ebp-0Ch]
7b6739f8 85f6           test    esi, esi

0:000> !heap -p -a 0x1694de98
    address 1694de98 found in
    _DPH_HEAP_ROOT @ 16871000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                16872270:         1694de98              168 -         1694d000             2000
          ? mfasfsrcsnk+134c
    7c59ab70 verifier!AVrfDebugPageHeapAllocate+0x00000240
    772f8fcb ntdll!RtlDebugAllocateHeap+0x00000039
    7724bb0d ntdll!RtlpAllocateHeap+0x000000ed
    7724b02f ntdll!RtlpAllocateHeapInternal+0x0000022f
    7724adee ntdll!RtlAllocateHeap+0x0000003e
    755a7610 msvcrt!malloc+0x00000090
    7b5bf417 mfasfsrcsnk!DllCanUnloadNow+0x00003897
    7b5aedac mfasfsrcsnk!DllGetClassObject+0x0001143c
    7b5add9e mfasfsrcsnk!DllGetClassObject+0x0001042e
    7b5ad59a mfasfsrcsnk!DllGetClassObject+0x0000fc2a
    7b5a5e59 mfasfsrcsnk!DllGetClassObject+0x000084e9
    7b5b74c5 mfasfsrcsnk!DllGetClassObject+0x00019b55
    002c12c1 AsfReaderHeap!CASFReader::CreateASFContentInfo+0x00000101 [t:\projects\cpp\wmf\wmf\wmf\casfreader.cpp @ 111]
    002c1145 AsfReaderHeap!CASFReader::OpenASFFile+0x00000055 [t:\projects\cpp\wmf\wmf\wmf\casfreader.cpp @ 52]
    002c1f71 AsfReaderHeap!fuzzme+0x00000031 [t:\projects\cpp\wmf\wmf\wmf\wmf.cpp @ 249]
    002c20c0 AsfReaderHeap!main+0x000000e0 [t:\projects\cpp\wmf\wmf\wmf\wmf.cpp @ 301]
    002c3987 AsfReaderHeap!__scrt_common_main_seh+0x000000fa [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
    76d16359 KERNEL32!BaseThreadInitThunk+0x00000019
    77277b74 ntdll!__RtlUserThreadStart+0x0000002f
    77277b44 ntdll!_RtlUserThreadStart+0x0000001b

    second 

0:000> !heap -p -a 0x1f544e98   
    address 1f544e98 found in
    _DPH_HEAP_ROOT @ 16871000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                16873e04:         1f544e98              168 -         1f544000             2000
          ? mfasfsrcsnk+134c
    7c59ab70 verifier!AVrfDebugPageHeapAllocate+0x00000240
    772f8fcb ntdll!RtlDebugAllocateHeap+0x00000039
    7724bb0d ntdll!RtlpAllocateHeap+0x000000ed
    7724b02f ntdll!RtlpAllocateHeapInternal+0x0000022f
    7724adee ntdll!RtlAllocateHeap+0x0000003e
    755a7610 msvcrt!malloc+0x00000090
    7b5bf417 mfasfsrcsnk!DllCanUnloadNow+0x00003897
    7b5aedac mfasfsrcsnk!DllGetClassObject+0x0001143c
    7b5add9e mfasfsrcsnk!DllGetClassObject+0x0001042e
    7b5ad59a mfasfsrcsnk!DllGetClassObject+0x0000fc2a
    7b5a5e59 mfasfsrcsnk!DllGetClassObject+0x000084e9
    7b5b74c5 mfasfsrcsnk!DllGetClassObject+0x00019b55
    002c12c1 AsfReaderHeap!CASFReader::CreateASFContentInfo+0x00000101 [t:\projects\cpp\wmf\wmf\wmf\casfreader.cpp @ 111]
    002c1145 AsfReaderHeap!CASFReader::OpenASFFile+0x00000055 [t:\projects\cpp\wmf\wmf\wmf\casfreader.cpp @ 52]
    002c1f71 AsfReaderHeap!fuzzme+0x00000031 [t:\projects\cpp\wmf\wmf\wmf\wmf.cpp @ 249]
    002c20c0 AsfReaderHeap!main+0x000000e0 [t:\projects\cpp\wmf\wmf\wmf\wmf.cpp @ 301]
    002c3987 AsfReaderHeap!__scrt_common_main_seh+0x000000fa [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
    76d16359 KERNEL32!BaseThreadInitThunk+0x00000019
    77277b74 ntdll!__RtlUserThreadStart+0x0000002f
    77277b44 ntdll!_RtlUserThreadStart+0x0000001b       

and the last one is different from the others:

            _DPH_HEAP_ROOT @ 16871000
in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                            16873a28:         1f56af70               90 -         1f56a000             2000
      ? mfasfsrcsnk+1694
7c59ab70 verifier!AVrfDebugPageHeapAllocate+0x00000240
772f8fcb ntdll!RtlDebugAllocateHeap+0x00000039
7724bb0d ntdll!RtlpAllocateHeap+0x000000ed
7724b02f ntdll!RtlpAllocateHeapInternal+0x0000022f
7724adee ntdll!RtlAllocateHeap+0x0000003e
755a7610 msvcrt!malloc+0x00000090
7b5bf417 mfasfsrcsnk!DllCanUnloadNow+0x00003897
7b5aef5e mfasfsrcsnk!DllGetClassObject+0x000115ee
7b5cf01d mfasfsrcsnk!DllCanUnloadNow+0x0001349d
7b5ba3f8 mfasfsrcsnk!DllGetClassObject+0x0001ca88
7b5ad7b3 mfasfsrcsnk!DllGetClassObject+0x0000fe43
7b5a5e59 mfasfsrcsnk!DllGetClassObject+0x000084e9
7b5b74c5 mfasfsrcsnk!DllGetClassObject+0x00019b55
002c12c1 AsfReaderHeap!CASFReader::CreateASFContentInfo+0x00000101 [t:\projects\cpp\wmf\wmf\wmf\casfreader.cpp @ 111]
002c1145 AsfReaderHeap!CASFReader::OpenASFFile+0x00000055 [t:\projects\cpp\wmf\wmf\wmf\casfreader.cpp @ 52]
002c1f71 AsfReaderHeap!fuzzme+0x00000031 [t:\projects\cpp\wmf\wmf\wmf\wmf.cpp @ 249]
002c20c0 AsfReaderHeap!main+0x000000e0 [t:\projects\cpp\wmf\wmf\wmf\wmf.cpp @ 301]
002c3987 AsfReaderHeap!__scrt_common_main_seh+0x000000fa [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
76d16359 KERNEL32!BaseThreadInitThunk+0x00000019
77277b74 ntdll!__RtlUserThreadStart+0x0000002f
77277b44 ntdll!_RtlUserThreadStart+0x0000001b

when we check one of the functions related with its allocation call stack we can see:

Line 1  int __stdcall sub_7B5AE710(int a1, void *Buf2, int a3)
Line 2  {
Line 3      (...)
Line 4      if ( !memcmp(ASF_Stream_Properties_Object, Buf2, 0x10u) )
Line 5      {
Line 6        vulnObject = (char *)objectMalloc(0x90u);// XXX fixed alloc
Line 7        if ( !vulnObject )
Line 8          return 0x8007000E;
Line 9        _vulnObject = (_DWORD *)vulnObjectContructor(vulnObject);
Line 10       __vulnObject = _vulnObject;
Line 11       if ( !_vulnObject )
Line 12         return 0x8007000E;
Line 13       v168 = (*(int (__thiscall **)(_DWORD, _DWORD *, PDWORD *, int))*_vulnObject)(// 0x7b5af870 - GUID comp
Line 14                *(_DWORD *)*_vulnObject,
Line 15                _vulnObject,
Line 16                &dword_7B589140,
Line 17                a3);     

The ASF_Stream_Properties_Object object has smaller size and is being here treated as an ASF_Extended_Stream_Properties_Object object. That object type confusion leads the function CASFPacketParser::CreateExtensionSystemsList at line 33 to read a value out of the object's bounds. Later based on this value, the loop at lines 58-62 is controlled. Depending on the attacker's ability to control the heap above, the type confusion can lead to a memory leak or memory corruption which can allow an attacker to achieve remote code execution.

Crash Information

(58d4.3018): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00d069d4 ecx=abababab edx=00000000 esi=abababab edi=00000000
eip=7b5a0770 esp=010ffc08 ebp=010ffc14 iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010282
mfasfsrcsnk!DllGetClassObject+0x2e00:
7b5a0770 8b4e04          mov     ecx,dword ptr [esi+4] ds:002b:abababaf=????????
0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************


KEY_VALUES_STRING: 1

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.Sec
    Value: 0

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

    Key  : Analysis.DebugData
    Value: CreateObject

    Key  : Analysis.DebugModel
    Value: CreateObject

    Key  : Analysis.Elapsed.Sec
    Value: 7

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

    Key  : Analysis.System
    Value: CreateObject

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 1653226

    Key  : Timeline.Process.Start.DeltaSec
    Value: 23


EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 7b5a0770 (mfasfsrcsnk!DllGetClassObject+0x00002e00)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: abababaf
Attempt to read from address abababaf

FAULTING_THREAD:  00003018

PROCESS_NAME:  WMF.exe

READ_ADDRESS:  abababaf 

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

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  00000000

EXCEPTION_PARAMETER2:  abababaf

STACK_TEXT:  
WARNING: Stack unwind information not available. Following frames may be wrong.
010ffc14 7b5a0fbb 00000000 00d06948 010ffc66 mfasfsrcsnk!DllGetClassObject+0x2e00
010ffc2c 7b673a77 00000000 010ffc54 010ffc64 mfasfsrcsnk!DllGetClassObject+0x364b
010ffc98 7b672400 00d03fc0 00d042d0 00000000 mfasfsrcsnk!MFCreateASFStreamingMediaSink+0x45477
010ffcb4 7b6836bf 00d03fc0 010ffcd0 00000000 mfasfsrcsnk!MFCreateASFStreamingMediaSink+0x43e00
010ffcd4 7b60e1f0 00d042d0 00d06ab0 011f7020 mfasfsrcsnk!MFCreateASFStreamingMediaSink+0x550bf
010ffd1c 00c313d3 00d06a50 00d03ec0 010ffdd0 mfasfsrcsnk!MFCreateASFStreamPrioritization+0x20f0
010ffd50 00c31165 00d03a78 010ffdd8 010ffdd0 WMF!CASFReader::CreateASFSplitter+0x93
010ffd70 00c31f71 011f75d8 010ffdb8 6b369ff8 WMF!CASFReader::OpenASFFile+0x75
010ffdfc 00c320c0 011f75d8 12901598 011f0000 WMF!fuzzme+0x31
010ffe50 00c33987 00000002 011f7020 011f61d0 WMF!main+0xe0
010ffe98 76d16359 00ec5000 76d16340 010fff04 WMF!__scrt_common_main_seh+0xfa
010ffea8 77277b74 00ec5000 22868102 00000000 KERNEL32!BaseThreadInitThunk+0x19
010fff04 77277b44 ffffffff 77298f27 00000000 ntdll!__RtlUserThreadStart+0x2f
010fff14 00000000 00c33a0f 00ec5000 00000000 ntdll!_RtlUserThreadStart+0x1b


STACK_COMMAND:  ~0s ; .cxr ; kb

SYMBOL_NAME:  mfasfsrcsnk!DllGetClassObject+2e00

MODULE_NAME: mfasfsrcsnk

IMAGE_NAME:  mfasfsrcsnk.dll

FAILURE_BUCKET_ID:  INVALID_POINTER_READ_c0000005_mfasfsrcsnk.dll!DllGetClassObject

OS_VERSION:  10.0.18362.239

BUILDLAB_STR:  19h1_release_svc_prod1

OSPLATFORM_TYPE:  x86

OSNAME:  Windows 10

FAILURE_ID_HASH:  {6721a602-9996-8910-8d1c-08e5b792ff24}

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

0:000> lmv a eip
Browse full module list
start    end        module name
7b580000 7b6b8000   mfasfsrcsnk   (export symbols)       C:\WINDOWS\SYSTEM32\mfasfsrcsnk.dll
    Loaded symbol image file: C:\WINDOWS\SYSTEM32\mfasfsrcsnk.dll
    Image path: C:\WINDOWS\SysWOW64\mfasfsrcsnk.dll
    Image name: mfasfsrcsnk.dll
    Browse all global symbols  functions  data
    Image was built with /Brepro flag.
    Timestamp:        D199DF63 (This is a reproducible build file hash, not a timestamp)
    CheckSum:         00136EB8
    ImageSize:        00138000
    File version:     10.0.18362.207
    Product version:  10.0.18362.207
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    Information from resource tables:
        CompanyName:      Microsoft Corporation
        ProductName:      MicrosoftÆ WindowsÆ Operating System
        InternalName:     Media Foundation ASF Source and Sink DLL
        OriginalFilename: mfasfsrcsnk.dll
        ProductVersion:   10.0.18362.207
        FileVersion:      10.0.18362.207 (WinBuild.160101.0800)
        FileDescription:  Media Foundation ASF Source and Sink DLL
        LegalCopyright:   © Microsoft Corporation. All rights reserved.

Timeline

2019-10-31 - Vendor Disclosure
2019-11-06 - Vendor requested additional trigger input files
2019-11-18 - Talos provided additional trigger input file to vendor
2020-01-02 - 60+ day follow up
2020-01-06 - Vendor provided patch/release timeline
2020-02-10 - Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.