Chapter 5. CSS
Abstract:
Cascading Style Sheets is a language that defines the presentation of a document. Although Cascading Style Sheets was originally defined to be used only with Hypertext Markup Language, today it can also be used with most markup languages, including Extensible Markup Language User Interface Language and Scalable Vector Graphics, and with practically any Extensible Markup Language document that supports stylesheets. Cascading Style Sheets by itself has a lot of potential regarding the Web application attack surface. This chapter provides an overview of the different versions of Cascading Style Sheets, discusses various syntax rules, and reviews a variety of attacks based on Cascading Style Sheets. It also discusses syntax bugs that may allow users to obfuscate attacks at a higher level of complexity. After reading this chapter, you should understand how to create several types of attack vectors that may not require the use of JavaScript or any other scripting language.
Key words: Cascading Style Sheets, At-rule, Ruleset, Selector, Declaration, Property, Event selector, State selector, Attribute selector
Cascading Style Sheets (CSS) is a language that defines the presentation of a document. CSS was originally defined to be used with HTML. In fact, as you saw in
Chapter 2, CSS and HTML are closely linked and the evolution of CSS occurred in parallel with that of HTML. But today, CSS can also be used with most markup languages, including XUL and SVG, and with practically any XML document that supports stylesheets.
CSS exists in three major versions: CSS Level 1, Level 2.1, and Level 3. Today, all modern browsers try to follow CSS 2.1 rules, but unfortunately, some CSS parsers follow the CSS1 parsing rules that were changed in CSS2, probably in an effort to support basic CSS without knowing the rules were changed. Fortunately, this is not as common as incorrect implementations of the standard, as we will see in more detail in
Chapter 6.
CSS3 includes a lot of new features that enable some new attacks, but at the time of this writing it is still in development. As all major browsers support CSS 2.1, the changes made to the standard were implemented in CSS 2.1, but not in CSS3. As a consequence, some browsers that started to implement CSS3 have CSS3 feature support over CSS 2.1 rules. This is actually correct at some point, since CSS3 is still under development, so developers should not assume that CSS3 is ready to be implemented (however, some browsers have been doing it), and these differences have made incomplete implementations problematic in some cases, as we will discuss in the rest of this chapter.
Although we already discussed CSS obfuscation in
Chapter 2, we are devoting this chapter to CSS because CSS by itself has a lot of potential regarding the Web application attack surface. We review a variety of CSS-based attacks in this chapter and discuss a couple of syntax bugs that may allow us to obfuscate attacks at a higher level of complexity. After reading this chapter, you should understand how several types of attack vectors that may not require the use of JavaScript or any other scripting language are created.
Syntax
CSS has very interesting parsing rules that differentiate it from HTML and JavaScript in several ways.
First, CSS and JavaScript differ, in that, when JavaScript has a syntax error, the whole code is ignored, but when CSS has a parsing error the browser will try to evaluate it, ignoring the unsupported code. That forces JavaScript code to be valid. In this regard, CSS is more like HTML, since an HTML document will try to be evaluated in the best way it can, which means anything that does not look like CSS will be ignored and the parser will move on to the next CSS-like segment.
This is a relevant point, as it means we can inject CSS code into the middle of non-CSS code, and the parser will evaluate it all as CSS code. This enables such attacks as information leakage on several browsers (as we will see in the “Attacks” section of this chapter), but it also gives us the advantage of being able to insert garbage into the middle of code and keep it as valid CSS.
The following changes were made to the syntax/grammar from CSS1 to CSS2:
• CSS1 stylesheets could only be in 1-byte-per-character encodings, such as ASCII and ISO-8859-1. CSS 2.1 has no such limitation. In practice, there was little difficulty in extrapolating the CSS1 tokenizer and some UserAgents have accepted 2-byte encodings.
• CSS1 only allowed four hex digits after the backslash () to refer to Unicode characters, whereas CSS2 allows six. Furthermore, CSS2 allows a whitespace character to delimit the escape sequence. For example, according to CSS1, the string “abcdef” has three letters (abcd, e, and f), and according to CSS2, it has only one (abcdef).
• Similarly, newlines (escaped with a backslash) were not allowed in strings in CSS1.
Also, advantageous is the fact that between CSS1 and CSS2 the syntax rules changed, which means that in some edge cases a CSS1 parser will fail to parse a document in the same way as a CSS2/3 parser. This is particularly important, since some old browsers support some old parsing rules, some new browsers support some new parsing rules, Web servers support whatever they want, and these differences in parsing rule support allow an attacker to pass a vector over an otherwise safe filter. It is important to note that most of these differences are not obvious, so it is understandable why they were not noticed before implementations were made. Also note that changing these specifications could be dangerous because old implementations would need to change as well.
Now that you know a little about CSS parsing rules, let us review the general syntax of CSS. When we talk about CSS, we use terms such as declaration blocks and stylesheets. A stylesheet is what we find inside STYLE tags, and it is referenced in HTML by a LINK element with a rel attribute with the value stylesheet. A declaration block appears inside the STYLE attribute of an HTML element and it defines the style of the current element.
The general syntax rules of CSS dictate that you have to escape all new lines inside strings, and that if you want to escape a character, it has to be preceded by a slash (as in; or 0 × 5C) and followed by two to six hexadecimal characters, optionally followed by a white space.
So, the following examples represent two lowercase
a characters (
0 × 61):