Содержание статьи
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. Твой код выполнится в браузере каждого посетителя, который зашел на страницу.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
