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.*