Wednesday, September 29, 2010

IE8 CSS-based forced tweeting

A few weeks back, I published a demo that uses a serious Internet Explorer cross-origin violation to permit a malicious web page to force the visitor to make unwarranted tweets:

The post was light on technical details of how the attack works, so they will be filled in below. In addition, I'll quickly take care of the FAQ:

Q) Does this attack affect earlier versions of Internet Explorer, such as IE6?
A) Yes, the attack works verbatim on IE6 in my testing.

Q) When will a patch be available?
A) As far as I'm aware, Microsoft have not stated when users of IE6, IE7 and IE8
will be afforded protection. I'm not privy to any other details.

Q) Why drop this 0-day?
A) The rampant misuse of the term "0-day" is somewhat disappointing. You can look
it up on Wikipedia, but an "0-day" is a term reserved for a disclosure where the
vendor was previously unaware of it. In this instance, the bug is better described
as a "500+ day" if you look at when the bug was first publicly disclosed.

So how does the Twitter attack work?
When exploiting this bug in general, you are looking to meet various conditions:

  • Finding a text injection point in the target origin to start a CSS property.

  • Using a text injection context that does not escape vital CSS characters.

  • Arranging for some sensitive information (such as an anti-XSRF token) to appear after the text injection point.

  • Arranging that no CSS property terminator appears before the sensitive information.

In the Twitter attack, the injection point can clearly be seen; it's simply the text of a tweet in my "scarybeaststest" account:

If you look at the raw source HTML of that page on, you'll see the following piece of HTML:

<span class="entry-content">{}body{font-family:"</span>

That's the start of a CSS descriptor injected into the Twitter page. Note that none of the characters {, }, : or " need to be escaped in the HTML tag value context -- there is no Twitter bug here.

The need for the " character in this injection context is subtle: it derives from how the IE CSS parser determines end-of-property. Essentially, IE is just looking for a ; character to denote the end of the value for the "font-family" property. Unluckily for us, the ; character appears in the Twitter HTML prior to the high-value anti-XSRF secret we are trying to steal. This would tend to terminate the injected CSS property early and spoil our day. Fortunately, ; is perfectly acceptable within a quoted section, so the use of " will ensure the stray ; characters appear between a pair of quotes and do not terminate the parse of our CSS property.

With our injection, the Twitter page ends up looking a little bit like:

<html>bla..bla..{}body{font-family:"..more HTML prop="value" some ; SECRET_XSRF_TOKEN blah EOF

The IE CSS parser keeps track of opening and closing quote pairs, and only terminates the CSS property if a ; is encountered outside of quoting. Our usage of a " character to start the property ensures that any ; characters appear within the quoted context. Eventually, the CSS parser includes the secret anti-XSRF token in the CSS property value and then hits EOF with an unterminated quote. IE happily recovers from this situation by constructing a CSS property value with all the text parsed so far!

So the attacker simply includes the above CSS-property-injected Twitter page as CSS and recovers the CSS value for the "font-family" property, which includes the secret token (as well as a bunch of quoted semi-colons and other HTML detritus which is less interesting to us).

There is quite a pile of IE CSS parsing bugs involved to pull this off. Many are CSS specification violations:

  1. Newlines permitted within quoted strings.
  2. Whitespace permitted outside quoted strings.
  3. Multiple quoted strings permitted in single property.
  4. End of file within quoted string does not invalidate descriptor.
  5. Bad Content-Type and non-CSS content ignored for cross-origin CSS.
As be seen, the interaction of CSS property terminator vs. quoting context is quite subtle. There might well be a simpler injection to use, but this more complicated example is simply the first working one I found.