This vulnerability manifests itself within the quartz.dll module located within the \Windows\System32 directory. This DLL is part of Microsoft's DirectShow multimedia framework and is responsible for parsing various media formats and handing data off to appropriate installable compressors and decompressors. Frequently, vulnerabilities in media formats exist within these installable compressors (see TPTI-09-01 and TPTI-09-02 for recent examples), however, in this case the problematic code is located within quartz itself. It should be noted that Quicktime does NOT need to be installed for this issue to be exposed.
Prior to Vista, DirectShow had support for parsing Apple's Quicktime format. This support was built upon DirectShow's COM-based architecture. DirectShow defines the IFilter interface that is used to implement filter graphs to render and perform miscellaneous operations on streams of media data.
When attempting to open a media file, quartz loops through different media types (defined as AM_MEDIA_TYPE structures, essentially GUIDs) and determines if the next node on the filter graph can handle the input stream's media type, negotiated via objects called Pins (see Mark Dowd and John McDonald's Media Frenzy presentation).
In practice, the Pin negotiation can be seen in a debugging session as a series of calls similar to this:
02d6f770 74837a7f quartz!CBaseMSRFilter::NotifyInputConnected+0x50
02d6f784 748340b2 quartz!CBaseMSRInPin::CompleteConnect+0x3a
02d6f79c 7483df8d quartz!CBasePin::ReceiveConnection+0xc2
02d6f7bc 7483e7d7 quartz!CBasePin::AttemptConnection+0x54
loop here until a successful connection
02d6f7e0 7483e36f quartz!CBasePin::TryMediaTypes+0x64
02d6f80c 7483e2f9 quartz!CBasePin::AgreeMediaType+0x73
02d6f824 7483e048 quartz!CBasePin::Connect+0x55In the case of this QuickTime DirectShow issue, when provided with a malicious file quartz determines the media type can be handled by the CQT class. We know that video data is handled in streams. Taking a look at the symbols contained within quartz that contains references to CQT, we see another interesting class called CQTStream. Below is a listing of the functions with symbols for this class:
CQTStream::BuildMediaType(long,CMediaType *)
CQTStream::CQTStream(ushort *,long *,CQT *,ushort const *,int)
CQTStream::ConvertInternalToRT(__int64)
CQTStream::ConvertRTToInternal(__int64)
CQTStream::DecideBufferSize(IMemAllocator *,_AllocatorProperties *)
CQTStream::GetAvailable(__int64 *,__int64 *)
CQTStream::GetDuration(__int64 *)
CQTStream::GetEndOfChunk(long,long,long)
CQTStream::GetMaxSampleSize(void)
CQTStream::GetMediaType(int,CMediaType *)
CQTStream::GetStreamLength(void)
CQTStream::GetStreamStart(void)
CQTStream::IsFormatSupported(_GUID const * const)
CQTStream::MapByteOffsetToSample(long,long *)
CQTStream::MapSampleToChunk(long,long *,long *,SampleToChunk * *)
CQTStream::MapSampleToTime(long)
CQTStream::MapTimeToSample(long,long *)
CQTStream::OnActive(void)
CQTStream::RecordStartAndStop(__int64 *,__int64 *,double *,_GUID const * const)
CQTStream::RefTimeToSample(CRefTime)
CQTStream::SampleToRefTime(long)
CQTStream::UseDownstreamAllocator(void)
CQTStream::`vector deleting destructor'(uint)
CQTStream::~CQTStream(void)We can see that the only functions here that take a MediaType as an argument are the BuildMediaType and GetMediaType functions. It's a safe bet to assume that they will be handling file data at a relatively lower level than some of the utility functions. Quickly disassembling GetMediaType shows that it is only 6 basic blocks and does nothing of interest to us.
Disassembling BuildMediaType shows more promise. Firstly, an interesting item to note, the presence of a stack cookie:
.text:748FB8B0 private: long __stdcall CQTStream::BuildMediaType(long, class CMediaType *) proc near
.text:748FB8B0
.text:748FB8B0
.text:748FB8B0
.text:748FB8B0 mov edi, edi
.text:748FB8B2 push ebp
.text:748FB8B3 mov ebp, esp
.text:748FB8B5 sub esp, 528h
.text:748FB8BB mov eax, ___security_cookie
.text:748FB8C0 mov [ebp+stackCookie], eaxIf a standard stack overflow were present in this function it might be a little bit more difficult to exploit. However, as we'll see this particular DirectShow issue is a more unique stack corruption vulnerability that will not be affected by the stack cookie mitigation.
A couple basic blocks into this function shows the first sign that it's parsing file data:
.text:748FB8EC loc_748FB8EC:
.text:748FB8EC mov eax, [ebx+1B8h]
.text:748FB8F2 cmp eax, 'ediv'
.text:748FB8F7 jz loc_748FBA9D
.text:748FBA9D loc_748FBA9D:
.text:748FBA9D push 22
.text:748FBA9F pop ecx
.text:748FBAA0 lea edi, [ebp+var_6C]
.text:748FBAA3 rep movsdThe 'vide' comparison here is a test for Apple's Quicktime image compression type. Following the successful branch we arrive at basic block that begins with a 22 byte seek, which, according to Apple's file format documentation, jumps over some extraneous structures and arrives at the very beginning of the ImageDescription ('stsd') atom.
This is where the vulnerability begins to manifest. Specifically, the next couple instructions are responsible for parsing the 'name' element of an ImageDescription structure. This field is a 32-character Pascal string, implemented as a 31 character string prefixed with a 1 byte length value. Herein lies the problem... if this length byte is larger than 31 characters an attacker can fool the code within quartz into writing a NULL byte beyond this string. The code responsible for this is shown below:
.text:748FBAA5 movsx eax, [ebp+pascalStrLen] ; the string length prefix byte
.text:748FBAA9 mov [ebp+eax+var_39], 0 ; attempted null terminateSo, this vulnerability allows a malicious media file to write a single NULL byte within 255 bytes in one direction of the stack variable var_39. Now comes the fun part, exploitation. Below is a WinDBG transcript demonstrating how this can be exploited:
0:017> bp quartz!CQTStream::BuildMediaType+0x1f5
Bp expression 'quartz!CQTStream::BuildMediaType+0x1f5' could not be resolved, adding deferred bp
0:017> g
Create thread 17:338
ModLoad: 76360000 76370000 C:\WINDOWS\system32\winsta.dll
ModLoad: 74810000 7497d000 C:\WINDOWS\System32\quartz.dll
ModLoad: 75f40000 75f51000 C:\WINDOWS\System32\devenum.dll
Breakpoint 0 hit
eax=65646976 ebx=01192bf0 ecx=00000000 edx=00000000 esi=01192b8e edi=01b9f08c
eip=748fbaa5 esp=01b9eb6c ebp=01b9f0a0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
quartz!CQTStream::BuildMediaType+0x1f5:
748fbaa5 0fbe45c6 movsx eax,byte ptr [ebp-3Ah] ss:0023:01b9f066=40The above line is showing the single length byte that comes directly from the file. Now, here is the NULL byte write which is attempting to terminate the Pascal string. The offset is stored in @eax and thus can cause the following memory write to seek past the string. At this point we can check the call stack to determine a good location to write the 0x00 byte. This is a contrived example as I have already chosen a location that is 0x40 bytes away from ebp-0x39, but for completeness the call stack follows.
0:017> k
ChildEBP RetAddr
01b9f0a0 748fc639 quartz!CQTStream::BuildMediaType+0x1f5
01b9f154 748387f0 quartz!CQT::CreateOutputPins+0x705
01b9f770 74837a7f quartz!CBaseMSRFilter::NotifyInputConnected+0x50
01b9f784 748340b2 quartz!CBaseMSRInPin::CompleteConnect+0x3a
01b9f79c 7483df8d quartz!CBasePin::ReceiveConnection+0xc2
01b9f7bc 7483e7d7 quartz!CBasePin::AttemptConnection+0x54
01b9f7e0 7483e36f quartz!CBasePin::TryMediaTypes+0x64
01b9f80c 7483e2f9 quartz!CBasePin::AgreeMediaType+0x73
01b9f824 7483e048 quartz!CBasePin::Connect+0x55
...So, the quickest location to attempt an overwrite is the return address within the stack frame at 0x01b9f0a0. The return address is currently 0x748fc639. By changing a single byte in this, we can cause the process to return to address space that can be reached via a javascript heap fill (in the context of a browser). This makes for a simple exploit technique that can be made fairly reliable (except of course if we're dealing with a DEP-enabled process in which case a more advanced exploitation technique is required). So, let's see what happens when we overwrite a single byte of that return address.
0:017> t
eax=00000040 ebx=01192bf0 ecx=00000000 edx=00000000 esi=01192b8e edi=01b9f08c
eip=748fbaa9 esp=01b9eb6c ebp=01b9f0a0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
quartz!CQTStream::BuildMediaType+0x1f9:
748fbaa9 c64405c700 mov byte ptr [ebp+eax-39h],0 ss:0023:01b9f0a7=74Here is the before:
0:017> dd 01b9f0a0 L2
01b9f0a0 01b9f154 748fc639After the NULL write:
0:017> dd 01b9f0a0 L2
01b9f0a0 01b9f154 008fc639So, now if we let the process go at this point it will return to 0x008fc639 which should not be mapped memory.
0:017> u 008fc639
<Unloaded_i.dll>+0x8fc638:
008fc639 ?? ???
^ Memory access error in 'u 008fc639'
0:017> g
(674.f0): 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=01173e38 ecx=0000930b edx=00090608 esi=01192bf0 edi=01192dd0
eip=008fc639 esp=01b9f0b4 ebp=01b9f154 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
<Unloaded_i.dll>+0x8fc638:
008fc639 ?? ???
0:018> !address @eip
008c0000 : 008c6000 - 000fa000
Type 00020000 MEM_PRIVATE
State 00002000 MEM_RESERVE
Usage RegionUsageHeap
Handle 008c0000At this point it's game over, a heap spray can easily reach this address. However, exploit mitigation techniques such as DEP would prevent this method as the pages of memory would not have the execute bit set and thus this would throw an access violation even if code was present at that address. A more advanced exploit could use Alexander Sotirov and Mark Dowd's .NET trick to overwrite a different portion of the return address and return to a loaded module controlled by the attacker, but that is out of the scope of this post.
On a related note I just returned from Sao Paulo, Brazil where I spoke at the You Sh0t the Sheriff conference on the discovery and exploitation of vulnerabilities in 3rd party codecs as well as delving into the inner workings of DirectShow. The slides should be uploaded to the DVLabs Appearances page next week.
The YSTS event was very informative and I will be writing a blog post soon covering the presentations I had the pleasure of attending.
--
Aaron
