Теоретические аспекты

    Как известно, основной метод борьбы с XSS - это фильтрация специальных символов во входном или выходном потоке. При этом традиционно фильтруются символы < >, а также " ' & %. Все эти символы имеют ASCII код меньший 128, и поэтому, в большинстве кодировок кодируются одним байтом, значение которого совпадает с ASCII-кодом символа.

    Но это справедливо не для всех кодировок. Рассмотрим такую кодировку, как UTF-7 . Эта кодировка создавалась для передачи данныx в системах, которые игнорируют старший бит в байте символа (в основном это почтовые сервисы). В этой кодировке используются символы + - ! а также буквы латинского алфавита и цифры. Остальные символы кодируются цепочками вида +XXX- где XXX - некоторая последовательность латинских букв. Так, например, символ < кодируется как +ADw-, символ > как +AD4-.

    Поскольку, знак + фактически никогда не фильтруется, то произвольный текст (в том числе и HTML-код) в кодировке UTF-7 беспрепятственно проходит сквозь  серверные скрипты.

    Попадая в пользовательский браузер, этот текст, при определеных условиях, может быть расшифрован браузером и восприняться как HTML-код, что влечет за собой возможность проведения XSS атаки. Если атакующему удастся "убедить" браузер клиента в том, что страница представлена в кодировке UTF-7, то XSS можно считать состоявшимся.

    Рассмотрим условия, при которых браузер может воспринять текст как UTF-7 кодировку. Все дальнейшие примеры приводятся для IE6. Другие барузеры наверняка используют другую систему декодирования, и поэтому приведенный код, почти наверняка, в них работать не будет.

    Рассмотрим механизм определения браузером кодировки загруженной страницы. Прежде всего, браузер анализирует поле Content-Type из заголовка HTTP ответа сервера. Если это поле содержит информацию о кодировке страницы, то браузер именно ее и использует для расшифровки текста. Например, такой заголовок Content-Type: text/html; charset=windows-1251 свидетельствует о том, что кодировка страницы - win-1251.

    Далее, если в теле HTML страницы присутствует тег <meta>, в котором указана кодировка страницы, то браузер изменяет текущую кодировку на указанную. Это происходит даже если в заголовке HTTP была указана другая кодировка. При этом, даже та часть страницы, которая уже проинтерпретировалась (до тега <meta>) перекодируется в соответствии с новой кодировкой. Несмотря на то, что преимущество тега <meta> над полем Content-Type явно не оговорено в стандартах языка, тем не менее IE поступает именно так. Например тег <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"> заставляет рассматривать страницу в кодировке ISO8859-1, даже если в загловке HTTP было указано Content-Type: text/html; charset=windows-1251.

    Если в теле страницы, тег <meta http-equiv="Content-Type"> встречается несколько раз, то во внимание принимается только первый из них. Остальные игнорируются (пример).

    В дальнейшем пользователь может поменять кодировку уже загруженной страницы, воспользовавшись пунктом Вид/Кодировка главного меню браузера.

    Теперь рассмотрим поведение браузера, если ни в заголовке HTTP, ни в метатегах, кодировка не указана. Если в браузере выствлен флажок Автовыбор кодировки (Вид/Кодировка/Автовыбор), то браузер пытается сам определить кодировку страницы. В процессе загрузки, он анализирует цепочки символов, и если встречается цепочка в известной браузеру кодировке, то он считает что вся страница представлена именно в этой кодировке.

    В процессе  определения кодировки, просматривается не весь текст HTML а только содержимое тегов. Значения атрибутов тегов - не просматриваются (либо просмтривается, но позже, после загрузки всего текста). То есть такую страницу <font>Мама мыла раму</font> браузер распознает как windows-1251, а вот такую <font title= "Мама мыла раму">Happy birthday to you</font>  - нет. Кроме того, просматриваются только первые 4 килобайта HTML-кода. Остальная часть на кодировку влияния не оказывает и будет интерпретирована в той кодировке, которая была определена по первым четырем килобайтам.

    Следует отметить, что распознавание кодировки, парсинг HTML тегов, и выполнение встретившихся скриптов происходит одновременно. Таким образом, если кодировка еще не определена, и встречается скрипт, зашифрованный в некоторой кодировке, например UTF-7, то браузер считает, что кодировка документа UTF-7, расшифровывает и выполняет скрипт, и только затем парсит оставшуюся часть документа. При этом, даже если впоследствии окажется, что кодировка иная (например встретился метатег с явным указанием кодировки, или были обнаружены русские буквы), скрипт все равно выполнится (пример).
Практические аспекты

    В качестве примеров, рассмотрим несколько форумных движков на предмет подверженности XSS атакам с помощью кодировки UTF-7.

    Ниже приведено начало HTML-страницы одного из самых распространенных в мире форумов -  vBulletin(версия 3.0.7, пример взят с одного из известных зарубежных сайтов):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" lang="en">
<head>
<meta name="description" content="Post your question about installing or upgrading to vBulletin 3.  Be sure to search, and check the installation docs before you post!" />
<title>vBulletin Community Forum - vBulletin 3 Installation and Upgrades</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<meta name="generator" content="vBulletin 3.0.7" />
<meta name="keywords" content="vbulletin,forum,bbs,discussion,jelsoft,bulletin board" />
<meta name="description" content="This is a discussion forum powered by vBulletin. To find out about vBulletin, go to http://www.somesite.com/ ." /> ....

    Заголовок HTTP Content-Type данного сайта не устанавливал кодировку (что, в общем-то, характерно для англоязычных сайтов), что делалает его потенциально уязвимым к описываемой атаке.

    Как видим, в странице указан метатег с кодировкой ISO-8859-1, что однозначно определяет кодировку страницы. Однако обратим внимание на то, что тег <title>, куда заносится тема текущего топика, находится перед метатегом (отмечено желтым). А это значит, учитывая описанные особенности парсинга, что если в тег title будет внедрен скрипт в кодировке UTF-7, то он будет раскодирован и выполнен. Таким образом, для проведения XSS атаки, достаточно создать в форуме тему, название которой будет зашифровано в кодировке UTF-7 и содержать вредоносный скрипт.

    Для корректного внедрения скрипта, нужно закрыть тег <title>, а затем внедрить тег <script>. В целом, внедряемый эксплойт может выглядеть так: </title><script>alert(document.cookie);</script> , что в кодировке UTF-7 выглядит следующим образом:

+ADw-/title+AD4APA-script+AD4-alert(document.cookie)+ADsAPA-/script+AD4-

    Как видим, эта строка не содержит ни одного фильтруемого символа, и без проблем может быть использована для внедрения в форум. В результате создания темы с таким именем, тег <title> примет вид:

<title>vBulletin Community Forum - +ADw-/title+AD4APA-script+AD4-alert(document.cookie)+ADsAPA-/script+AD4-</title>

    Результирующая HTML-страница созданной темы можно посмотреть на примере (еще раз обращу внимание, что для срабатывания эксплойта, страница должна просматриваться в IE с включенным режимом автоопределения кодировки). Как видим, скрипт действительно срабатывает, и может быть использован для отправки на CGI-сниффер куков любого пользователя, просматривающего созданную тему.

    Кроме того, внедренный HTML-текст может содержать любые теги, что позволяет не только отправить куки на сайт атакующего, но и скрыть факт внедрения вредоносного скрипта на страницу. Например, внедрение такой строки +ADw-/title+AD4APA-script+AD4-alert(document.cookie)+ADsAPA-/script+AD4-+ADw-comment+AD4-делает текст, идущий после строки, содержимым тега <comment>, что сделает страницу просто невидимой (пример ). Примечательно еще и то, что в таком случае, вся страница оказывается в кодировке UTF-7, поскольку метатег, определяющий истинную кодировку страницы, оказывается внутри комментария, и, естественно, не обрабатывается. Таким образом, при постинге сообщений в данную ветку форума, можно вставлять произвольный HTML-текст в формате UTF-7, и этот текст действительно будет парсится как HTML (пример ).

    Описанная уязвимость характерна не только для vBulletin 3.*, но и практически для всех распространенных форумных движков, например vBulletin 2.* ,IPB 1.*, IPB 2.*, Phorum, и других. Во всех этих движках, тема ветви форума вставляется в тег <title>, а сам тег titleидет перед метатегом кодировки.

    Более того, многие англоязычные версии форумов вообще не устанавливают ни заголовок HTTP Content-Type, ни метатег <meta http-equiv="Content-Type">, видимо надеясь на то, что латинские буквы нормально отобразятся в любой кодировке. В результате - они все подвержены данной уязвимости. Единственное условие срабатывания для них XSS атаки - это подходящий тип и настройки пользовательского браузера ( IE + автовыбор кодировки ).

    Исключение тут составляет phpBB, который не подвержен данной уязвимости, поскольку, видимо случайно, тег <meta http-equiv="Content-Type"> предшествует тегу <title>:

....<meta http-equiv="expires" content="0">
<meta name="page-type" content="Software-Download">
<meta name="robots" content="INDEX,FOLLOW">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>MySQL Problem :: phpBB.de</title>
<link rel="stylesheet" href="/css.css" type="text/css">
<style type="text/css">....

    Возможная область действия данной атаки, естественно, не ограничивается форумами. Ведь аналогичные эксплоиты могут бытьвнедрены практически в любые динамически обновляемые страницы. Это и чаты и BBS, и почтовые сервисы, гостевые книги, блоги, он-лайн журналы и т.д. А ведь подавляющее число этих сервисов никак не фильтруют символы UTF-7, и фактически допускают свободное внедрение XSS в этой кодировке.

    В заключение, хотелось бы остановиться на подверженности описанной уязвимости русскоязычных сайтов . Как ни странно, но они более защищены от атаки, чем англоязычные. Во-первых потому, что русскоязычные сайты (да и вообще сайты, использующие национальные кодировки)  гораздо чаще используют задание кодировки непосредственно на сервере, и передают ее через HTTP Content-Type, что делает атаку невозможной. И кроме того, особенности парсинга HTML не дают сработать UTF-7 кодировке, если в телетегов, в первых 4kb текста, встретится киррилица. Это связано с тем, что буквы национального алфавита, как правило, имеют ASCII-код более 127, а такие коды не могут быть использованы в 7-битном UTF-7, и потому браузер не сможет интерпретировать страницу в этой кодировке.

    Впрочем, последнее преимущество не всегда очевидно. Дело втом, что современные форумы очень громоздки, и первые 4kb, как правило, занимают блоки CSS и скриптов, и не содержат в себе тегов с кириллицей. Таким образом, даже если на странице присутствует киррилица, это еще не означает, что сайт не подвержен UTF-7 атаке.