We have just updated Security Advisory 2488013 for the publicly-disclosed Internet Explorer CSS vulnerability. It now reflects the fact that limited attacks attempting to exploit this vulnerability are present in-the-wild. The advisory also includes a new workaround that can help protect your computers until a security update is available. This workaround is different from the workarounds that we typically recommend, and so we wanted to give you more detail about it here.
Vulnerability Recap
This vulnerability requires an attacker to provide a CSS style sheet that includes a reference to itself with an @import command. When Internet Explorer tries to load this recursive style sheet, it corrupts memory in a way that could be exploited for arbitrary code execution. Unfortunately, there is no way to selectively disable this functionality, which is why the best workaround up to this point is to enable EMET to block aspects of the known exploits from being successful.
The new workaround
This workaround is an MSI package (Microsoft “FixIt”) that uses the Windows application compatibility toolkit to make a small change to MSHTML.DLL every time it is loaded by Internet Explorer. This change causes Internet Explorer to refuse to import a CSS style sheet if it has the same URL as the CSS style sheet from which it is being loaded. Simply put, the workaround inserts a check to see if a style sheet is about to be loaded recursively, and if it so, it aborts the load of the style sheet. You can read more about the Windows infrastructure that allows this type of workaround here: http://technet.microsoft.com/en-us/library/cc748912(WS.10).aspx
It’s important to note that the workaround will protect Internet Explorer only if the latest security updates have been applied, including MS10-090 which was released on December 14, 2010. You can find MS10-090 at http://www.microsoft.com/technet/security/bulletin/MS10-090.mspx.
To install the workaround, click here: http://download.microsoft.com/download/E/5/6/E56904FD-3370-479D-B14A-E5481222C59C/MicrosoftFixit50591.msi
If you’d like to uninstall the workaround after you have installed it, click here: http://download.microsoft.com/download/3/3/3/33346329-840F-4B9F-B54E-9AE1114EA331/MicrosoftFixit50592.msi
How the workaround works
Internet Explorer represents CSS style sheets with an instance of the mshtml!CStyleSheet class. The CStylesheet Create() method is called on every style sheet import and has access to the URL of both the parent and child style sheets. To get the absolute URL of the child style sheet, it calls the function ExpandUrlWithBaseUrl, as in the following assembly and graphic:
mshtml!CStyleSheet::Create+0x197:
6ebb7065 50 push eax
6ebb7066 8d95f0dfffff lea edx,[ebp-2010h]
6ebb706c e8326a1a00 call mshtml!ExpandUrlWithBaseUrl (6ed5daa3)
The workaround replaces this function call with a call to a new function the workaround introduces. This new function does the following things:
- Calls ExpandUrlWithBaseUrl() to translate the relative URL to an absolute URL, just like the original code
- If ExpandUrlWithBaseUrl() returns an error, then the new function returns that error to CStyleSheet::Create()
- It ExpandUrlWithBaseUrl() succeeds, it then calls _wcsicmp() to see if the child’s absolute URL is equal to the parent’s absolute URL
- If they are equal, it returns 80004005h, which is an error code ExpandUrlWithBaseUrl() can return if it is unable to do the URL expansion
- If they are not equal, it returns 0, mimicking a successful ExpandUrlWithBaseUrl() call that CStyleSheet::Create() would have made
Now you may ask, where is this new function implemented? The workaround overwrites a function which is only used on process shut down to clean up debugging resources. It changes the first instruction to a ret, so normal calls to this function will simply return, and then implements the workaround check. Here’s a graphic representing the new flow:
What the workaround changes look like
After the workaround is applied, the relevant part of CStyleSheet::Create() is updated to:
mshtml!CStyleSheet::Create+0x197:
6ebb7065 50 push eax
6ebb7066 8d95f0dfffff lea edx,[ebp-2010h]
6ebb706c e8a0f51b00 call mshtml!DeinitScriptDebugging+0x1 (6ed76611)
Note it calls into DeinitScriptDebugging() + 1.
DeinitScriptDebugging() is changed to:
mshtml!DeinitScriptDebugging:
//when this function is called by other code, return immediately
6ed76610 c3 ret
//call ExpandUrlWithBaseUrl() to translate the child’s relative URL to an absolute one
6ed76611 50 push eax
6ed76612 52 push edx
6ed76613 50 push eax
6ed76614 e88a74feff call mshtml!ExpandUrlWithBaseUrl (6ed5daa3)
//if the call failed, return the failure code
6ed76619 85c0 test eax,eax
6ed7661b 751a jne mshtml!DeinitScriptDebugging+0x2f (6ed76637)
//call _wcsicmp() to compare the parent and child’s absolute URLs
6ed7661d 5a pop edx
6ed7661e 8b12 mov edx,dword ptr [edx]
6ed76620 58 pop eax
6ed76621 e8748ff8ff call mshtml!_wcsicmp (6ecff59a)
//if they are equal, return 80004005h, otherwise, return 0
6ed76626 85c0 test eax,eax
6ed76628 7405 je mshtml!DeinitScriptDebugging+0x27 (6ed7662f)
6ed7662a 31c0 xor eax,eax
6ed7662c c20400 ret 4
6ed7662f b805400080 mov eax,80004005h
6ed76634 c20400 ret 4
6ed76637 5a pop edx
6ed76638 5a pop edx
6ed76639 c20400 ret 4
Why the workaround is safe to install
The workaround does all of the following checks before modifying MSHTML.DLL:
- File version of MSHTML.DLL is as expected
- Checksum of MSHTML.DLL is as expected
- All of the assembly instructions that will be replaced are exactly as expected
This ensures that it is not applied to the wrong version of MSHTML.DLL and that the results of the change are what were intended by the workaround. If a certain MSHTML.DLL does not pass all of these checks, it will not be modified.
Applying this workaround will not interfere with the installation of the final security update to address this issue. However, applying the workaround will have a small effect on the startup time of Internet Explorer. In our testing, we found that it added approximately 150ms to the process start time. Therefore, as you are applying the final security update, you should uninstall the workaround as it will no longer be needed. We also recommend that you test this workaround with any internal line-of-business applications before deploying it. The final security update to address this issue will be fully tested and ready for broad deployment.
What about CSS style sheet loops?
During our investigation, the question came up: what about A.css importing B.css which then imports A.css? In other words, would a CSS style sheet loop trigger the vulnerability too? Fortunately, through testing and code review we’ve determined that this configuration of CSS style sheets will not trigger the vulnerability, so simply checking that the child style sheet has a different absolute URL from the parent style sheet is sufficient to detect and block attacks.
Acknowledgements
Special thanks to Bruce Dang, Jonathan Ness, and Matt Miller for their work on this workaround. Thanks to Robert Hensing for his championing of this type of workaround starting in 2009. (/wave Rob)
- Kevin Brown, MSRC Engineering
*Posting is provided “AS IS” with no warranties, and confers no rights.*