Содержание статьи
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
XSS (Cross-Site Scription) — внедрение вредоносного кода на JavaScript в веб‑приложение. Атакующий ищет способ нарушить логику исполнения и вклинивается между приложением и пользователем. Результат атаки — выполнение действий от имени пользователя, кража данных (включая cookie), подмена любых данных на сайте, фишинг, перенаправление на сторонние ресурсы, основа для других векторов атаки.
Есть три основных типа XSS:
- reflected (отраженная) — возникает, когда данные из запроса подставляются в ответ сервера без должной фильтрации;
- stored (хранимая) — хакер может добавить вредоносный код в базу данных, который без фильтрации будет выведен на странице приложения;
- DOM-based (основанная на DOM) — при этой уязвимости у атакующего есть возможность передать код на выполнение функции
eval(или в тег) innerHTML.
Все три типа XSS основаны на одной ошибке — плохой фильтрации данных, на которые пользователь может повлиять.
Отраженные XSS
Представь, что тебе на почту прилетел промокод 01234 от какого‑то интернет‑магазина. Переходишь по ссылке в письме и видишь: «Ваш персональный промокод 01234, дает скидку 50%». Приятная скидка, но ты замечаешь параметр ?promo=01234 в URL. Пробуешь поменять на OLJSxksa858, и надпись меняется на «Ваш персональный промокод OLJSxksa858, дает скидку 50%». Пахнет прямым отражением параметра в контенте страницы. «Прямое отражение» значит, что сервер берет строку и без каких‑то серьезных манипуляций вставляет ее в ответ.
Первое — смотришь исходник в инспекторе объектов браузера, чтобы понять верстку и построить стратегию для теста:
<h3>Ваш персональный промокод OLJSxksa858, дает скидку 50%</h3>Следующий шаг — вбить пейлоад для тестирования XSS: <. Жмешь Enter и видишь, как поверх страницы появляется сообщение со строкой Pwned. Поздравляю, ты нашел reflected XSS. То есть XSS, которая вызвана прямым отражением значения параметра из запроса в ответе сервера.
Смотри, как это может выглядеть на стороне сервера. Например, веб‑приложение на PHP с промокодами:
$promo = $_GET['promo'];echo "<h3>Ваш персональный промокод: $promo, дает скидку 50%</h3>";Скрипт выводит значение прямо в HTML, без какой‑либо фильтрации или санитизации. Код HTML, который браузер получит от сервера, будет выглядеть так:
<h3>Ваш персональный промокод <script>alert("Pwned")</script>, дает скидку 50%</h3>Браузер, может, и решит, что разработчик не в своем уме, но команду выполнит. И это не совсем шутка: современные браузеры стараются защитить пользователей от подобных инъекций, но механизм защиты слабый. У браузера нет маркеров, чтобы надежно отделить код инъекции от кода, написанного разработчиком.
info
Chrome и Firefox могут блокировать всплывающие окна команд alert(, prompt( и confirm(. Это защита от спама и вредоносных скриптов. Для подтверждения XSS лучше используй print( или console..
Алгоритм поиска reflected XSS выглядит так. Проверяем все возможные текстовые поля и параметры URL. Вводи уникальную ерунду, которой точно не будет на странице, например OISJDflkj349sdkljf0304. Когда получишь ответ сервера, жми Ctrl-F и ищи свою строку.
Возможно, придется закрыть тег или выйти за рамки текущего тега, внося изменения в HTML. Стоит об этом узнать заранее. Для этого пробуй добавить в строку спецсимволы: <, ', ", / и прочие. В ответе смотри, что произошло с указанными символами. Если видишь, что символы превратились в HTML-entity (< вместо <) или еще как‑то изменились, скорее всего, на сервере есть фильтры или санитизация ввода. Если экранирования не произошло, ты на 99% наткнулся на уязвимость. Пробуй собрать рабочий пейлоад.
Тестировать лучше всего в Burp. Браузер может искажать вывод в инспекторе объектов или исходном коде страницы. Например, открой игру XSS game area, которую в Google разработали специально для изучения XSS. В первой задаче отправь что‑нибудь вроде <. При просмотре исходника HTML в браузере ты увидишь, что произошла подмена на \, и ошибочно решишь, что сайт безопасен. На самом деле подмену выполнил браузер! Отправь <, чтобы увидеть разницу.

Если хочешь работать в браузере, смотри вкладку «Сеть/Network». В сырых данных response искажений нет.

Чтобы собрать рабочий пейлоад, внимательно изучай HTML-код сайта. Иногда достаточно вставить <, иногда нужно добавить обработку события или что‑то еще. В примере с игрой Google задача решается просто:
<script>alert("Pwnded")</script>В других случаях это не пройдет. Смотри на то место, где происходит вставка, и думай, как можно внедрить скрипт.

www
Собрать рабочий пейлоад тебе помогут примеры в читшите по XSS от PortSwigger.
Но вернемся к примеру с промокодами. Иногда разработчики добавляют промокоды в форму через скрытые поля. Например, так:
<input type="hidden" name="promo" value="012345" />Как вылезти за рамки инпута и нахулиганить?
Обычная кавычка (") может сломать разметку элемента: <. Появилась висящая кавычка. Браузер с ней справится и отбросит лишнее. Но теперь ты знаешь, что можешь управлять разметкой. Например, добавить событие для скрытого поля подобным пейлоадом:
" onfocus=alert(1) autofocus tabindex=1 accesskey="
Код формы примет такой вид:
<input type="hidden" name="promo" value="" onfocus=alert(1) autofocus tabindex=1 accesskey="" />Полезная нагрузка закрывает кавычку для value и добавляет обработку события onfocus. При загрузке контента поле постарается получить фокус и сработает событие. Свойство accesskey нужно, чтобы убрать висящую кавычку и не нервировать браузер.
info
Помни, что для браузера HTML-код — это просто текст, который нужно читать определенным образом, а подстановка происходит на уровне серверных скриптов. Твоя задача сводится к тому, чтобы разгадать, как происходит замена в тексте на сервере.
Каждый ресурс требует индивидуального подхода. В некоторых случаях script не пройдет фильтрацию. Тогда пробуй вариант через картинку: "> <. Серверный скрипт закроет висящую кавычку и отдаст корректный HTML. Браузер добавит картинку и попробует загрузить ее по адресу x. Естественно, это поведение вызовет ошибку, и сработает событие onerror, в котором заготовлен наш код.
Байпас запуска функций
Для вызова алерта я использовал alert(. В классическом варианте при вызове функции alert нужно указывать строку, то есть обернуть аргумент в кавычки. Но фильтры могут заметить инъекцию. Чтобы этого избежать, есть масса вариантов обхода. Вот несколько примеров, которые могут показаться неожиданными:
// Вызов без круглых скобокalert`1`;// Снова без кавычекalert(/1/);// Передаем массивalert([1]);// Обращение к объекту self как к массивуself["al"+"ert"](1);// То же, но через toptop["alert"](1);// Прячем вызов за ASCIIself[String.fromCharCode(97,108,101,114,116)](1);// То же, но через Base64self[atob`YWxlcnQ=`](1);// С использованием объекта globalThisglobalThis[[]+atob`YWxlcnQ=`](1);// Функцией source объекта RegEx получаем строку alertself[/alert/.source](1);Если фильтр работает по словарю запрещенных слов, тег script на 100% будет заблокирован. В этом случае попытайся понять, как именно происходит фильтрация. Ты можешь натолкнуться на фильтр, который выполняет только одну замену. Например, отправь пейлоад <. Сервер заменит первое вхождение script, и останется нужное тебе: <.
Иногда фильтр меняет все значения, но делает это в один проход. В этом случае проскочит <. Фильтр удалит script из центральной части, а крайние части сложатся в новый script.
Еще один способ байпаса — рандом‑кейс: ScRiPt. Подобную запись не увидит фильтр, который ищет точное совпадение.
Это малая часть вариантов, ищи как можно больше информации про обман фильтров и не стесняйся креативить при тестировании. Может быть, придумаешь что‑то, что еще никому не приходило в голову!
Владельцы сайтов редко серьезно воспринимают reflected XSS, считая это баловством. Что может произойти, если пользователь выведет в своем браузере сообщение или строчку в консоль? Но при определенных условиях отраженная XSS — мощное оружие в руках злоумышленника.
В 2015 году @mamyraimov написал твит о найденной уязвимости на сайте «Ламода». Это была обычная reflected XSS в поиске по сайту. Но для компании она несла колоссальные риски. Дело в том, что тогда в сети свободно гуляла слитая база покупателей «Ламоды». В базе были в том числе имейлы пользователей. Еще не догадался, в чем проблема?
Используя XSS, хакеры могли подменить HTML-код страницы и сформировать фишинговую форму или выполнить другую атаку. Все, что оставалось сделать, — провести рассылку по готовой базе. Но уязвимость пофиксили без лишнего шума.
XSS в десктопных приложениях
С ростом популярности веб‑технологий XSS вышли за рамки браузера. Многие кросс‑платформенные приложения созданы на основе технологий вроде Electron или Node.js, которые позволяют веб‑приложениям работать наравне с обычными программами.
Если авторы такого приложения не позаботились о фильтрации ввода и вывода, может всплыть XSS. В приложениях на Electron ситуация становится критической, если включена настройка nodeIntegration. Эта настройка дает доступ к Node.js API, что выводит приложение из веб‑песочницы сразу в ОС. Это прямой путь от XSS к RCE (Remote Code Execution). Для примера смотри информацию о CVE-2020-16608 в программе Notable версии 1.8.4.
Не думай, что этим грешат только мелкие проекты. Программисты из крупных и известных компаний тоже часто допускают такие оплошности. В 2020 году была найдена огромная дыра в популярном корпоративном мессенджере Slack. Slack долгое время работал с включенным nodeIntegration. Встроенный рендеринг позволял исполнить JavaScript — например, при отрисовке превью для внешних ссылок. Цепочка выполнения приводила к тому, что хакер мог запускать любую команду в операционной системе жертвы. В результате хакеры получали доступ к учетным данным администраторов и захватывали целые корпоративные сети.
Другие популярные десктопные приложения, в которых находили опасные XSS: Discord, Visual Studio Code, Skype, Signal Desktop, Everonet, Joplin. В популярном мессенджере WhatsApp Desktop находили множество XSS. Например, CVE-2019-11928.
Десктопные приложения — это не только то, что запускается на твоем компьютере. Терминалы оплат, киоски услуг и даже банкоматы чаще всего работают на базе ПК или неттопа. Приложения, в которые тыкает пользователь, часто представляют собой WebView (то есть встроенный браузер). Если есть WebView, есть угроза XSS.
Защититься от reflected XSS легко: фильтруй любой пользовательский ввод. Любой! Даже если это скрытые поля, которые генерирует твой код. Экранируй спецсимволы или заменяй их HTML-entity. Если твой код исправит < на \, у хакера почти не останется шансов.
Хранимые XSS
Простой пример хранимой XSS — комментарии на сайте. Пишешь обычный комментарий, но в конце добавляешь <. Обновляешь страницу, открываешь консоль и видишь свое сообщение. Комментарий с вредоносным пейлоадом сохранился в базе данных. Отсюда и название «хранимая», или stored. Твой код выполнится в браузере каждого посетителя, который зашел на страницу.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»
