Short Analysis of CVE-2021-33760

Update on Fuzzing WMF Metadata

Table of Contents

Foreword

It’s been a long while since I last posted because it’s been kind of hard to find time in these few months, but I’m finally back with some good news. A few months back we got an update for the “interesting thing” I mentioned in my last post, and Microsoft rewarded us with a small bounty for it. What we found was a mp3 file with metadata that could crash windows explorer when you navigated to the folder containing the file. We expanded on it and did some analysis on what was likely to be causing the crash, so here’s a short writeup on it, enjoy!

Discovering The Crash

We realized that there was a consistent crash when we were triaging all the crashes reported by WinAFL and amazingly enough both of us managed to fuzz the same type of crash on separate machines. Since the auto-triager that we coded tested our crashes against a barebones parsing application, we thought that if it crashed there it would also potentially crash windows explorer, which reads the same IPropertyStore object to grab metadata. When we accessed a folder containing our crash.mp3, windows explorer closed on its own.

We also made sure to test it against an actual installed windows environment instead of purely testing it in VMs because there have been instances where we caught crashes that only worked within VMs. We also realized some peculiar things about the bug:

  1. It does not always crash windows explorer upon navigating to the POC folder. We had some theories on this but we felt it was a memory-dependent thing that we unfortunately did not understand much as windows explorer does not always end up reading OOB or even reading the metadata at all. However, it does crash on most of the tests (>95% of our tests).
  2. If the file was in the “Recent Files” section of windows explorer, it pretty much crashes windows explorer on startup.
  3. The file can still be read, opened etc, it only causes a problem when its IPropertyStore object is parsed.

Nevertheless, we still thought it was an interesting crash so we delved deeper to try to find out exactly where it was crashing.

WMF Vulnerability (CVE-2021-33760)

This is a vulnerability that allows for an out-of-bounds read, which leads to information disclosure.

Revision record

Date Revision Version Change Description Author
24/02/2021 10.0.18362.1316 Vulnerability Report Brandon Chong and Cao Yitian of Starlabs

Summary Of The Vulnerability

The vulnerability is present in mfsrcsnk.dll, which is part of the Microsoft Media Foundation framework.
An integer underflow leads to an Out-of-Bounds (OOB) Read when parsing an MP3 frame header. The crash can be triggered by navigating into a folder containing poc.mp3.

Further Analysis

The crash occurs in mfsrcsnk.dll:

0:000> g
(56c8.7dc4): 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.
Time Travel Position: B8573:0
mfsrcsnk!CMPEGFrame::DeSerializeFrameHeader+0x42:
00007ffb`2629f872 418b0e          mov     ecx,dword ptr [r14] ds:000001c7`29218504=????????
0:000> k
 # Child-SP          RetAddr               Call Site
00 0000003f`dc7cde80 00007ffb`2629d50c     mfsrcsnk!CMPEGFrame::DeSerializeFrameHeader+0x42
01 0000003f`dc7cdf00 00007ffb`2629cb58     mfsrcsnk!CMP3MediaSourcePlugin::ReadMPEGFrameHeader+0x78
02 0000003f`dc7cdf70 00007ffb`2629e8bc     mfsrcsnk!CMP3MediaSourcePlugin::DoReadFrameHeader+0x5c
03 0000003f`dc7cdff0 00007ffb`2629f1fa     mfsrcsnk!CMP3MediaSourcePlugin::ParseHeader+0x1cc
04 0000003f`dc7ce0d0 00007ffb`2629f060     mfsrcsnk!CMFMP3PropertyHandler::FeedNextBufferToPlugin+0x12e
05 0000003f`dc7ce170 00007ffb`262992e3     mfsrcsnk!CMFMP3PropertyHandler::FeedBuffersToPlugin+0x9c
06 0000003f`dc7ce230 00007ffb`262a9da4     mfsrcsnk!CMFMP3PropertyHandler::InternalInitialize+0x103
07 0000003f`dc7ce300 00007ffb`5f400df9     mfsrcsnk!CMFPropHandlerBase::Initialize+0x84
08 0000003f`dc7ce360 00007ffb`5f3fdcfb     windows_storage!InitializeFileHandlerWithStream+0x175
09 0000003f`dc7ce420 00007ffb`5f43a345     windows_storage!CFileSysItemString::HandlerCreateInstance+0x2c7
0a 0000003f`dc7ce510 00007ffb`5f3de47a     windows_storage!CFileSysItemString::_PropertyHandlerCreateInstance+0xad
0b 0000003f`dc7ce5c0 00007ffb`5f3ece20     windows_storage!CFileSysItemString::LoadHandler+0x1aa
0c 0000003f`dc7ce710 00007ffb`5f3c9d95     windows_storage!CFSFolder::LoadHandler+0xe0
0d 0000003f`dc7cea70 00007ffb`5f3caeca     windows_storage!CFSPropertyStoreFactory::_GetFileStore+0x165
0e 0000003f`dc7ceb40 00007ffb`5f3cb042     windows_storage!CFSPropertyStoreFactory::_GetPropertyStore+0x20e
0f 0000003f`dc7cec30 00007ffb`5f3ca824     windows_storage!CFSPropertyStoreFactory::GetPropertyStore+0x22
10 0000003f`dc7cec70 00007ffb`5f3ca3cb     windows_storage!CShellItem::_GetPropertyStoreWorker+0x384
11 0000003f`dc7cf1b0 00007ffb`5fd09e3b     windows_storage!CShellItem::GetPropertyStore+0xdb
12 0000003f`dc7cf480 00007ff6`10d611ab     SHELL32!SHGetPropertyStoreFromParsingName+0x5b
13 0000003f`dc7cf4f0 00007ff6`10d6111d     harness!fuzzme+0x3b
14 0000003f`dc7cf540 00007ff6`10d615f4     harness!wmain+0x11d
15 0000003f`dc7cf7a0 00007ffb`611f7c24     harness!fuzzme+0x484
16 0000003f`dc7cf7e0 00007ffb`61aad721     KERNEL32!BaseThreadInitThunk+0x14
17 0000003f`dc7cf810 00000000`00000000     ntdll!RtlUserThreadStart+0x21
0:000> !heap -p -a @r14
    address 000001c729218504 found in
    _DPH_HEAP_ROOT @ 1c7290a1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             1c7290a5d68:      1c729214000             4000 -      1c729213000             6000
    00007ffb61b4462f ntdll!RtlDebugAllocateHeap+0x000000000000003f
    00007ffb61af501e ntdll!RtlpAllocateHeap+0x0000000000078cce
    00007ffb61a7b4bb ntdll!RtlpAllocateHeapInternal+0x0000000000000a1b
    00007ffb60209da0 msvcrt!malloc+0x0000000000000070
    00007ffb541aad4b MFPlat!operator new+0x0000000000000023
    00007ffb541a1d76 MFPlat!MFCreateMemoryBuffer+0x0000000000000056
    00007ffb2629f140 mfsrcsnk!CMFMP3PropertyHandler::FeedNextBufferToPlugin+0x0000000000000074
    00007ffb2629f060 mfsrcsnk!CMFMP3PropertyHandler::FeedBuffersToPlugin+0x000000000000009c
    00007ffb262992e3 mfsrcsnk!CMFMP3PropertyHandler::InternalInitialize+0x0000000000000103
    00007ffb262a9da4 mfsrcsnk!CMFPropHandlerBase::Initialize+0x0000000000000084
    00007ffb5f400df9 windows_storage!InitializeFileHandlerWithStream+0x0000000000000175
    00007ffb5f3fdcfb windows_storage!CFileSysItemString::HandlerCreateInstance+0x00000000000002c7
    00007ffb5f43a345 windows_storage!CFileSysItemString::_PropertyHandlerCreateInstance+0x00000000000000ad
    00007ffb5f3de47a windows_storage!CFileSysItemString::LoadHandler+0x00000000000001aa
    00007ffb5f3ece20 windows_storage!CFSFolder::LoadHandler+0x00000000000000e0
    00007ffb5f3c9d95 windows_storage!CFSPropertyStoreFactory::_GetFileStore+0x0000000000000165
    00007ffb5f3caeca windows_storage!CFSPropertyStoreFactory::_GetPropertyStore+0x000000000000020e
    00007ffb5f3cb042 windows_storage!CFSPropertyStoreFactory::GetPropertyStore+0x0000000000000022
    00007ffb5f3ca824 windows_storage!CShellItem::_GetPropertyStoreWorker+0x0000000000000384
    00007ffb5f3ca3cb windows_storage!CShellItem::GetPropertyStore+0x00000000000000db
    00007ffb5fd09e3b SHELL32!SHGetPropertyStoreFromParsingName+0x000000000000005b
    00007ff610d611ab harness!fuzzme+0x000000000000003b
    00007ff610d6111d harness!wmain+0x000000000000011d
    00007ff610d615f4 harness!fuzzme+0x0000000000000484
    00007ffb611f7c24 KERNEL32!BaseThreadInitThunk+0x0000000000000014
    00007ffb61aad721 ntdll!RtlUserThreadStart+0x0000000000000021

@r14 points to an invalid region on the heap.

Vulnerability Analysis

At CMP3MediaSourcePlugin::ParseHeader+0x314 (mfsrcsnk.dll+0xea04), the function CMP3MediaSourcePlugin::DoScanForFrameHeader() is called. This stores the value 0x2282 into the variable offset.

// buf = 0x000001c729214000, remaining_size = 0x00000000000022e6, &offset = 0x0000003fdc7ce060
hr = CMP3MediaSourcePlugin::DoScanForFrameHeader(MPEGFrame, buf, remaining_size, &offset);

At CMP3MediaSourcePlugin::ParseHeader+0x20e (mfsrcsnk.dll+0xe8fe) the variables remaining_size and buf are updated.

LABEL_29:
    LODWORD(v34) = offset;
    remaining_size -= offset; // 0x00000000000022e6 - 0x0000000000002282 = 0x0000000000000064 
    buf += offset;            // 0x000001c729214000 + 0x0000000000002282 = 0x000001c729216282
    goto LABEL_30;
}

At CMP3MediaSourcePlugin::ParseHeader+0x2d9 (mfsrcsnk.dll+0xe9c9), the function CMP3MediaSourcePlugin::DoReadFirstFrameBody() is called.

// buf=000001c729216282, remaining_size=0000000000000064, &offset=0000003fdc7ce060
hr = CMP3MediaSourcePlugin::DoReadFirstFrameBody(MPEGFrame, buf, remaining_size, &offset);

Eventually, the function CMPEGFrame::DeSerializeFrameBody() is called with the same arguments:

0:000> k
 # Child-SP          RetAddr               Call Site
00 0000003f`dc7cdee8 00007ffb`2629f789     mfsrcsnk!CMPEGFrame::DeSerializeFrameBody
01 0000003f`dc7cdef0 00007ffb`2629aaa1     mfsrcsnk!CMP3MediaSourcePlugin::ReadMPEGFrameBody+0x49
02 0000003f`dc7cdf60 00007ffb`2629e9ce     mfsrcsnk!CMP3MediaSourcePlugin::DoReadFirstFrameBody+0x41
0:000> r rcx, rdx, r8, r9
rcx=000001c72921bea0 rdx=000001c729216282 r8=0000000000000064 r9=0000003fdc7ce060

At CMPEGFrame::DeSerializeFrameBody+0x2fe5f (mfsrcsnk.dll+0x3f15f), as remaining_size is less than required_size, the check fails and the function immediately returns with HRESULT 0. The value of offset is not updated and remains 0x2282.

if ( body_tag == 'ofnI' ) {
    LODWORD(required_size) = required_size + 0x74;
    if ( remaining_size < required_size ) // required_size = 0x74
        goto LABEL_22;
}
LABEL_22:
    CallStackScopeTrace::~CallStackScopeTrace(v13);
    return hr;
}

At CMP3MediaSourcePlugin::ParseHeader+0x2f7 (mfsrcsnk.dll+0xe9eb), the variables remaining_size and buf are updated again. However, as remaining_size is an unsigned int, an integer underflow occurs, causing remaining_size to store a large value. Also, buf now points to an invalid heap region.

    LODWORD(v34) = offset;
    remaining_size -= offset; // 0x0000000000000064 - 0x0000000000002282 = 0x00000000ffffdde2
    buf += offset;            // 0x000001c729216282 + 0x0000000000002282 = 000001c729218504

At CMPEGFrame::DeSerializeFrameHeader+0x39 (mfsrcsnk.dll+0xf869), a check is performed. Since remaining_size contains a large value, the check is passed. Execution flow continues, causing an OOB Read and a crash when trying to access the invalid pointer stored in buf.

if ( remaining_size < 4 ) {
    ... // Irrelevant Code
}
v10 = *buf; // OOB Read

Conclusion

In the end, it was concluded that this bug was in fact not exploitable and was an information disclosure bug. This is not to make light of information disclosure bugs but it is slightly disappointing that this was not exploitable as I would definitely have liked to do further research into actually crafting an exploit poc and popping calc.exe.

Nevertheless, this does show that fuzzing does have great potential to produce viable crashes as well as even CVEs as long as the input corpus is extensive enough (and also filtered to remove any potential garbage inputs). This was a pretty fun venture as I have never really worked with windows when it comes to security before and it was intruiging trying to figure out the internal workings of windows libraries and even the windows kernel.

Afterword

This post has long been due and I do apologize for that but it has been really hectic these few months and I also haven’t been able to do much lately in this field as serving the military really is taking up a lot more time than I predicted months ago. I will try my best to finish up any posts that I had been meaning to complete and also continue writing new posts if I ever find anything interesting, but do expect the frequency of these posts to drop by a lot.

As always, it’s been a fun ride tearing my hair out analyzing the bug and its weird occasionally-not-crashing tendencies and I’m definitely happy that in the end I managed to claim a CVE for it.

But well, I suppose that’s all for now. Thanks for reading.

<

GreyHats WelcomeCTF 2023: Pwn

Derusting my Pwn

>

Fuzzing Windows Stuff: WMF

2. Windows Media Foundation: Metadata

đź“š