Monday, 15 February 2016

Stealing Secrets with CSS : Cross Origin CSS Attacks

In this post I'm going to discuss a web attack that was designated CVE-2015-5826 and CVE-2015-1287 which abuses the way CSS parsing works in some browsers and expands the way we think about HTML injection attacks.


CSS Cross Origin attacks work by constructing CSS style-sheets from vulnerable pages and extracting sensitive information from these pages in the form of CSS property attributes. Vulnerable pages include anything page that allows an attacker to inject arbitrary printable unhindered alphanumeric text including braces, brackets and parenthesis; basically any subset of the ASCII table that allows you to construct valid CSS. Attacks with an even more restricted character set may be possible depending on the content of the vulnerable page. Most modern browsers including Firefox, Chrome and Opera have recently included defenses for this attack although to outdated browsers this attack still poses a threat.

In order to have an HTML page treated as CSS the browser needs to operate in Quirks Mode, the following section explains what and how this "mode" is.

Quirks Mode

CSS parsers can be configured to run in a "greedy" or "best effort" mode; in which they ignore anything that isn't CSS syntax and gobble up everything else in an effort to deliver as user friendly and backward compatible experience as possible (if I had a 1 cent for every time a user experience decision caused a security vulnerability...). The idea comes from the days when browsers were far and few between (there was basically Netscape and Microsoft Explorer) and this meant there was little in the way of standardizing web scripting, well there wasn't any need to. The W3C then introduced standards in order to try (as futile as that try was) to civilize the web to some extent, but this meant that if browsers adopted these standards cold turkey there would be tons of web pages written according the old lore that would not work anymore; thus a special operating mode was introduced to accommodate these pages. This "special operating" mode is called Quirks mode and can be triggered by starting off an HTML document with an invalid Document Type Declaration.

For some browsers quirks mode introduces behavior more interesting than just ignoring faulty CSS, it affects the way Content-Type headers are interpreted. For instance in quirks mode, if CSS is loaded or referenced in a link or style element the Content-Type header will be ignored and the content returned will be interpreted as CSS!

To quote:

Of course what that means is that any kind of document can then be treated as CSS, which means as an attacker if you can inject a document into a style-sheet reference it gives him/her tremendous freedom! You might even be able to have an inject-able HTML document be treated as CSS itself :) That last sentence was pretty important, its actually the realization that this post is trying to share with you, if you don't completely get it please re-read it until you do.

Besides Quirks Mode ofcourse there's the full on standards compliant mode called Standards mode; this is only engaged when an HTML document is started with a valid DTD.  So if an HTML document starts off with a DTD that looks like this :

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
or any other valid DTD it will parse the entire document in Standard Mode. And the following DTDs are examples that will trigger quirks mode most modern browsers:


Basically any non-valid DTD. You can check which mode a browser is parsing HTML by inspecting the document.compatMode property via JavaScript; as follows:

document.compatMode == "CSS1Compat" ? "Browser is in Standards Mode" : "Browser is in Quirks Mode"

Here's some screenshots of that little script in action with some examples of DTDs that trigger the two parsing modes:

Browser parsing in Standards mode

Browser Parsing in Quirks Mode

And here's a summary of how different browsers behave according to my research on the subject:

Summary of how browsers respond to some  DTDs for HTML

*Q - Quirks Mode
*S - Standards Mode
*Tests were done using Firefox 44.0.2, Opera 35.0, Chrome 48.0  

Obviously upon finding out that Quirks Mode will force browsers to ignore content type headers I had to check it out for myself, here's a couple screenshots of me doing that:

In the screenshot above we can see Firefox loading up the HTML page shown in the bottom right, which references the CSS shown in the bottom left. I'm using PHP to force my Apache2 web server to return a Content-Type header of text/plain which means its just basic text. And as you can see the browser's console appropriately warns us that some text is trying to be loaded as CSS.

Exactly the same situation as the previous screenshot except that the DTD of the HTML page being loaded forces the browser into Quirks Mode and as a result the CSS (being served with a text/plain Mime type) is parsed and applied to the text on the page!

The Setup

So that is the awesomeness that is Quirks Mode, lets talk about the vulnerability for a second. In this scenario we have a simple page vulnerable to injection. We need to clear a few rules of engagement here though, when I say "vulnerable to injection" I mean the following:

  • only alphanumeric,quotes ",' and braces {}, brackets [] and parenthesis ()
  • we can exclude any active content injection from this case as well
  • In this example I'm using the same origin to host the attack page, this is merely for demonstration purposes and because all the browsers I currently have include protections for the attack.
So basically you have a page in which you can influence the text content. Where's the page I'm going to use for this demonstration:

Obviously there is no hindrance here to a full on XSS attack, you would just need to imagine that there's some hectic WAF blocking anything that could be used to execute XSS.

The Attack

The Attack works as follows, we want to bypass same origin policy in order to steal the secret embedded on the page. You can think of this secret as a Cross Site Request Forgery (CSRF) token, credit card number, password etc. We are going to do this by forcing our victims browser to use the inject-able page as CSS on a page we control. The attack will extract the information by having the piece of information we are targeting interpreted as an attribute of a CSS property i.e. a font-family name or background image URL value. So in summary the attacker prepares a page that includes the vulnerable page as a style sheet, and extracts the information (usually using JavaScript).
Lets see how this works


We're going to use the following injection payload:


this will cause the page to render as follows:

Which means we effectively have a page that when interpreted as CSS will have a font-family name
of "secret goes here". It looks good, all we need to do now is host this injected page on a page under our control and extract the CSS property using JavaScript.


In order to get hold of the font-family name we just created we need to host the injected page as a CSS Stylesheet also remembering that we need to force the style sheet to be loaded under Quirks Mode! So pay attention to the DTD of the attack page. Here's the little page I prepared for this example:

Once its loaded up we can extract any CSS properties by using the following DOM path:


Where i,j are the indices needed to reach the desired cssText property. In our case it will be i=j=0. Where's what it looks like when the information has been extracted correctly and loaded up into the DOM for extraction:

Secret extracted successfully as a font-family name

Boom! Secret extracted game over :)

References and Further Reading

  1. Lin-Shung Huang, Lin-Shung Huang, Chris Evans, Collin Jackson; Protecting Browsers from Cross-Origin CSS Attacks [ACM Conference on Computer and Communications Security (CCS) 2010]
  2. Wikipedia , Quirks Mode [retrieved 2/14/2016]
  3. @filedescriptor Cross-Origin CSS Attacks Revisited (feat. UTF-16) [retrieved 2/14/2016]
  4. Mozilla Developer Network, Quirks Mode and Standards Mode [retrieved 2/14/2016]
  5. Mozilla Developer Network, Mozilla Quirks Mode Behavior [retrieved 2/14/2016]
  6. CVE-2015-5826 
  7. CVE-2015-1287