Всем привет! В данной статья я хочу рассказать про XSS уязвимости, какие они бывают и откуда их можно ждать. Сразу хочу сказать, что статья предназначена скорее для новичков в теме и не претендует на уникальность или очень глубокое погружение в тему.
Так как я работаю в банке и последнее время занимаюсь разработкой фронта системы авторизации и аутентификации пользователей, мне приходится довольно много времени уделять безопасности приложения, потому что последнее чего хочет клиент банка — это компрометации его авторизационных данных:). Поэтому я решил собрать все свои знания и опыт в этой области в кучу и поделиться ими с вами. Ну и вообще тема безопасности сейчас кажется очень актуальной, тк мы чуть ли не каждую неделю слышим истории об утечках данных даже у самых крупных и прогрессивных российских IT компаний.
Что такое XSS
Пересказывая своими словами определение с Википедии, XSS (Cross‑site scripting) — это тип уязвимости, встречающийся в web приложениях. XSS атаки позволяют внедрить вредоносный скрипт (или как его еще часто называют эксплойт) на страницу приложения, в результате чего у пользователей, посещающих эту страницу, могут украсть данные разной степени чувствительности: куки, сессионные токены, логины с паролями и просто личную информацию о пользователе.
Внедрить эксплойт злоумышленники могут различными способами, например оставить комментарий под постом или товаром в онлайн магазине, содержащий скрипт. И, если разработчики web‑приложения не позаботились о валидации данных, то вредоносный скрипт запустится у всех пользователей, открывших комментарии на странице. Такой тип уязвимости называется «сохраняемый», но подробнее об этом чуть позже.
Также, наверно, более популярный способ, когда злоумышленник передает вредоносный пэйлоад прямо в ссылке на наше приложение в параметрах запроса или в хэше, который читается в JS и может быть выполнен. Чаще всего это «отраженные» либо «основанные на DOM» XSS атаки, о них тоже чуть позже.
Справедливости ради стоит сказать, что в современных приложениях, написанных на современных фреймворках (где в основном экранируются данные пользовательского ввода) становится всё меньше и меньше возможностей совершить XSS атаку. Также и разработчики браузеров не сидят на месте и улучшают в них безопасность, например с помощью таких вещей как:
SOP (Same‑Origin Policy) — политика позволяющая определить, какие стрипты будут иметь доступ к данным, а какие нет, точнее даже с каких ресурсов мы разрешаем скриптам получать данные пользователя. Простыми словами помогает запретить скриптам с одного домена получить доступ к данным пользователя от другого домена.
CSP (Content Security Policy) — политика, которая позволяет установить список доверенных источников скриптов, все скрипты из других источников просто будут проигнорированы.
Валидация входных данных — современные браузеры также самостоятельно валлидируют входные данные и не позволяют нагло пробрасывать скрипты, экранируя их.
Но не смотря на все это XSS всё ещё возможен, да и в целом полезно знать о таких вещах, в конце концов даже на собеседовании могут спросить:)
Типы XSS атак
Я хочу рассмотреть 3 основных типа XSS (на самом деле их больше, но эти 3 типа наиболее распространены):
Сохраняемый — когда вредоносный скрипт через пользовательский ввод сохраняется в БД приложения и потом тем или иным способом запускается у клиента, посетившего страницу, которая генерится на основе данных из той самой БД.
Отраженный — при такой уязвимости база не участвует в доставке вредоносного скрипта жертве. Эксплойт нигде не хранится, атака обычно происходит через URL, когда например вредоносный скрипт пробрасывается через query параметры запроса, «отражается от сервера», где в процессе формирования страницы скрипт добавляется в тело ответа и пользователь получает страницу с уже встроенным в неё скриптом.
XSS на основе DOM — особенность данного типа атаки в том, что она эксплуатирует уязвимости DOM (очевидно). В отличии от двух других типов, в этом страница на сервере не меняется. Нам приходит совершенно безопасный HTML, но JS, который уже запустится на клиенте, отработает неправильно из‑за внедренного в него скрипта. Другими словами, главное отличие XSS на основе DOM в том, что эксплойт добавляется на страницу в рантайме, в момент запуска JS и он никогда не покидает пределы браузера.
Давайте теперь поподробнее разберем каждый из типов.
Stored XSS (Хранимая XSS)
Как я уже говорил ранее, сохраняемый XSS взаимодействует с жертвой через сервер приложения. Легче всего объяснить принцип работы с помощью простого примера. Представьте, что наше приложение — это маркетплэйс, на котором размещаются различные товары, в каждом товаре есть раздел отзывов, в котором каждый пользователь может оставить собственно отзыв. Если разработчики не побеспокоились о безопасности данного раздела, а реализован он примерно так:
function addReviews() {
// В примере я намеренно использую var, тк сложно себе представить в современном приложении, использующем ES6 синтаксис такую реализацию :)
var reviews = getReviews();
reviews.forEach(function (review) {
var reviewsList = document.getElementById("reviews");
reviewsList.innerHTML += "<li>" + review + "</li>";
})
}
addReviews()
<ul id=«reviews»></ul>
В таком случае злоумышленник может добавить свой отзыв:
Товар говно
<script>
alert(document.cookie);
</script>
Далее у каждого, кто посмотрит этот комментарий отобразится только «Товар говно», но помимо этого выскочит alert со всеми его куками (вместо этого злоумышленник скорее всего сделает запрос, в теле которого передаст все добытые данные о пользователе к своему серверу).
Такая уязвимость направлена на большое количество пользователей, потому что распространяется она, ну скажем, естественным способом, скрипт запустится у всех, кто посетит страницу. В отличии от «отраженного» XSS, для распространения которого часто нужно применять социальную инженерию.
Пример, который я описал выше конечно максимально примитивный, и может показаться, что сегодня такую уязвимость словить нереально, но я нашел прошлогоднюю статью, где парень отловил «сохраняемую» XSS уязвимость в Microsoft Teams в 2021 году.
Microsoft Teams — Cross Site Scripting (XSS) Bypass CSP | by Numan Turle | Medium
В данном случае в проекте было много легаси, доставшегося Microsoft от Skype, но никогда не знаешь, где окажешься завтра. Хотя на самом деле я не знаю, где вы находитесь и сегодня, может у вас на проекте тоже много легаси и стоит проверить реализацию старых решений:)
Reflected XSS (Отраженная XSS)
В отраженном XSS реализация доставки вредоносного скрипта выглядит иначе. Скрипт не должен сохраняться на серверах приложения, он попадает жертве через ссылку. Из этого следует очевидный вывод, что каждому отдельному пользователю, на которого осуществляется атака, нужно дать такую ссылку лично, например по почте или во время личной переписки, хотя ссылка может быть размещена на сайте злоумышленника, на который тем или иным способом попала жертва и тыкнула на неё. Но, опять же, скорее всего на этот сайт вы попали по ссылке из email'а или из личной переписки.
Дальше по этой ссылке (в которой в query параметрах зашит скрипт) мы попадаем на страничку, которую сформировал сервер, отталкиваясь от содержания ссылки, и добавляя в страничку все те параметры, что в ней имеются. Не трудно догадаться, что скрипт, который добавил злоумышленник в параметры, тоже попадет в сформированный HTML и благополучно запустится у жертвы. Тут уже злоумышленник может отправить себе ваши куки или собрать другие чувствительные данные со страницы и тоже отправить их себе.
Конечно скрипт не из любого query параметра попадет на страницу и запустится, у нас должна быть ещё и «особая» реализация работы с этим параметром в приложении. В качестве примера хочу привести не самую стандартную ситуацию, но зато это случай из жизни, который демонстрирует, что даже сегодня можно запросто проморгать такую уязвимость.
В нашем приложении на фронте активно использовались query параметры, на их основе мы понимали, какие именно данные нужно будет отобразить клиенту, какой это тип пользователя, через мобильное ли приложение он открыл нашу страницу или через обычный браузер, ну и т.д. В нашем приложении был SSR и все данные, полученные из query параметров мы просто складывали в стор. Он у нас вместе с другим самым необходимым кодом инлайново добавлялся в HTML и отправлялся клиенту. Таким образом, если пробросить в один из query параметров скрипт, он без проблем оказывался в финальном HTML, формированием которого занимался сервер.
По сравнению с сохраняемым XSS, данная уязвимость имеет меньший охват, так как атаке подвергается только тот, кто перешел по ссылке со скриптом. В то время как сохраняемой XSS атаке подвергается любой, кто посетил страницу, на которой разместили эксплойт. Но и обнаружить такую уязвимость сложнее, так как её не получится выявить с помощью статического анализа.
DOM-Based XSS (XSS на основе DOM)
Такой тип XSS атак нацелен непосредственно на внедрение скрипта в DOM дерево нашего приложения именно во время отработки JS. Например как и в случае с отраженным XSS, мы можем пробросить вредоносный скрипт через query параметр. Но, в отличии от предыдущего примера, наше приложение не добавит этот скрипт в HTML и вернет пользователю страничку без эксплойта.
После того как клиент загрузил страницу и все необходимые для её работы файлы, у клиента запускается JS, который для тех или иных целей обращается к document.location, чтобы достать оттуда данные из query параметра (в котором лежит наш вредоностный скрипт) и например добавить эти данные на страницу пользователя. После этого скрипт запускается, имея в свою очередь доступ к личным данным пользователя.
Учитывая то, что в современной веб индустрии большинство приложений — это SPA, реализованные на React, Angular, Vue и других фреймворках, где большАя часть логики перенесена на сторону клиента и страницы формируются именно на основе работы JS в браузере, XSS уязвимости на основе DOM встречаются чаще всего.
Давайте разберем пример:
Представьте, что мы работаем над банковским веб-приложением, в котором есть раздел ответов на вопросы, каждый из ответов в нем выглядит как отдельная статья. Банк довольно большой, соответственно вопросов к нему тоже много:) В какой‑то момент принимается решение, что для удобства нужно сделать поиск по разделу:
<form>
<label for="search">Поиск:</label>
<input type="text" id="search-box" name="search-box">
<button type="submit" id="search-button">Найти</button>
</form>
Но также необходима возможность делиться ссылкой на результат поиска. Например, если клиент банка обращается в службу поддержки, чтобы сотрудник мог сразу предоставить ссылку с найденными ответами по интересующей клиента теме.
function updateSearchQueryParam(value) {
const queryParams = new URLSearchParams(window.location.search);
queryParams.set('search', value);
const newPath = window.location.pathname + '?' + queryParams.toString();
history.pushState(null, '', newPath);
}
function updateSearchSubtitle() {
const searchFromQuery = new URLSearchParams(window.location.search).get('search');
const searchSubtitle = document.getElementById('searchSubtitle');
if(searchFromQuery) {
searchSubtitle.innerHTML = 'Результаты поиска по запросу' + searchFromQuery;
}
}
Функцию updateSearchQueryParam
мы вызываем каждый раз, когда совершаем поиск, чтобы записать в query параметр то, что ищем. А функцию updateSearchSubtitle
также вызываем при каждом поиске, а также при загрузке страницы, чтобы если в query параметре что‑то было, мы это отобразили.
Вот в этот момент в нашу реализацию пробралась уязвимость. Увидев параметр поиска в ссылке и то, что его содержимое попадает на страницу, мы можем попробовать передать скрипт с alert
и увидеть уведомление на странице. Вместо alert
мы можем сделать что‑то пострашнее и например отправить себе куки пользователя.
Кстати говоря, такую уязвимость всё ещё можно отследить на стороне сервера. Если мы пишем логи всех запросов, в них будет видно, что приходил подозрительный запрос со скриптом в значении одного из query параметров. Но, как я говорил ранее, бывают случаи, когда скрипт не покидает границ браузера. Например, если в нашем приложении мы работаем не с query параметром, а с hash. Как известно, то что мы пишем в hash ссылке не улетает на сервер, но JS без проблем может работать с тем, что мы туда передали.
Другие типы XSS атак
Скажу ещё пару слов о других типах XSS атак, они не так распространены, но думаю знать об их существовании будет не лишним.
mXSS или XSS с мутациями — довольно свежий вид XSS атаки, о котором впервые упомянули в 2013. Для реализации такой атаки нужны глубокие познания в том, как работают браузеры и какие механизмы они используют для борьбы с XSS.
Этот вид атаки эксплуатирует механизм очистки и санитайзинга пользовательского ввода браузером. Таким образом с виду нерабочий скрипт, после прохождения очистки браузером становится вполне валидным и может причинить ущерб клиенту, да и компании, в целом.
Blind XSS (Слепая XSS) — это подмножество сохраняемого XSS, только запуститься эксплойт может далеко не сразу и даже не обязательно в том же приложении. Например, через форму обратной связи, злоумышленник отправляет отзыв или вопрос, в который встраивает скрипт. Далее сотрудник службы поддержки открывает данное сообщение, после чего и запускается скрипт. Как вы понимаете, это запросто может быть другое приложение, какой‑нибудь сервис для администрирования нашего сайта.
Self XSS — вид атаки, при котором жертва самостоятельно запускает вредоносный скрипт на своем устройстве, в основном, в консоли браузера. Выглядит это примерно так: злоумышленник делает рассылку в соцсетях, по почте или даже в личном сообщении, где, в целях безопасности, или чтобы помочь взломать сайт (мол сделай это и получишь доступ к учетке своей бывшей), предлагает открыть консоль и ввести код, который скорее всего отправит ваши персональные данные мошенникам, вот собственно и всё. Такую уязвимость по правде говоря сложно вообще назвать уязвимостью, так как это просто социальная инженерия.
На этом краткий обзор окончен, в другой статье погружусь в тему уже поглубже и расскажу, как искать XSS уязвимости и самое главное, как с ними бороться.
UPD: Благодарю mrkaban за здравое замечание по поводу нэйминга и по поводу забытой мной Self XSS, все добавил, все поправил.
Комментарии (8)
mrkaban
00.00.0000 00:00+7Стоит указывать названия XSS на английском языке, так как именно эти названия считаются общепринятыми:
Reflected XSS (Отраженная XSS)
Суть атаки заключается в том, что страница берет параметр, который ей передается пользователем в запросе, и возвращает его пользователю без каких-либо изменений в ответе. Если ответ при этом форматируется тегами (или попадает в скрипт), он будет отображен с учетом форматирования. Весь этот процесс можно считать основной сигнатурой данной атаки.
Например, помещаем параметр target_host=<script>alert(1)</script>, но нужно еще привести пользователя на эту страницу.
Stored XSS (Хранимая XSS)
Именно хранимая, а не сохраненная.
Суть данной атаки заключается в том, что у злоумышленника есть возможность сохранять на сервере код, который вернется пользователю, если он откроет страницу; сами данные сохраняются неформатированными. При этом происходит срабатывание сохраненного payload злоумышленника, реализуется код XSS, который возвращается пользователю.
Простой пример, кто-то разместил в комментарии вот такой код:
<img src=1 onerror=alert(1)>
DOM-Based XSS
Отличительной особенностью данного вида XSS является то, что payload не отображается в коде страницы, которая возвращается пользователю, то есть не передается серверу. Поэтому обнаружить данный вид XSS, основываясь только на основе анализа возвращаемого кода, затруднительно.
Возможные источники (sources в иностранной терминологии), которые могут быть использованы злоумышленником:
● document.URL
● document.documentURI
● document.URLUnencoded (IE 5.5 or later Only)
● document.baseURI
● location
● location.href
● location.search
● location.hash
● location.pathname
● window.name
● document.referrer
Blind XSS (слепая xss)
Возникает, когда данные, вводимые злоумышленником, сохраняются на сервере, но отображаются в другой части приложения или вообще в другом приложении.
Например, обратная связь или премодерация комментариев через админку.
Self XSS
Данная атака реализуется только в том случае, если ее выполнит сам пользователь. Уязвимость при этом присутствует в функциях, которые доступны только владельцу (пользователю) аккаунта.
Например, так называемый "взлом" facebook, примерно в 2014 году, когда пользователям рассылали сообщения, мол для вашей безопасности нужно открыть в браузере консоль и ввести туда определенный код.
dunai12 Автор
00.00.0000 00:00+3Спасибо, хороший поинт с нэймингом, поправлю обязательно. И добавлю про Self XSS, совсем про него забыл, хоть это и сложно считать настоящей XSS уязвимостью, но знать полезно.
mrkaban
00.00.0000 00:00+2да в принципе довольно странная атака, в том плане, что попросить пользователя что-то сложное сделать, а не отправить ссылку. И самое смешное, что есть куча примеров того, что это работает...
Можно долго спорить к чему её отнести, так как с таким же успехом можно и AnyDesk попросить поставить.
SoreMix
00.00.0000 00:00На самом деле почему нет, всё зависит от ситуации.
Представим self-xss которая находится где нибудь в био профиля пользователя, при этом другие пользователи не могут открыть любой другой профиль, кроме своего. Связав такую self-xss и csrf login/logout, можем получить следующий вектор:
Атакующий делает self-xss в профиле -> жертва переходит на сайт с эксплоитом -> csrf logout -> csrf login в аккаунт атакующего -> перенаправляем жертву по URL с профилем -> т.к. жертва в аккаунте атакующего, срабатывает XSS. Дальше можно допустим слать запросы на другой домен данной компании, на какой нибудь путь с sensetive информацией, и т.к. домен доверенный и если он добавлен в CORS, прилетят интересующие нас данные, а дальше уже шлем их атакующему. Ну на крайний случай да хоть фейк логин форму сделать.
В целом, это считается self-xssmrkaban
00.00.0000 00:00+4Атакующий делает self-xss в профиле -> жертва переходит на сайт с эксплоитом -> csrf logout -> csrf login в аккаунт атакующего -> перенаправляем жертву по URL с профилем -> т.к. жертва в аккаунте атакующего, срабатывает XSS.
Это уже Content spoofing, а не Self XSS.
Content spoofing (также именуемая content injection и virtual defacement) – атака, при которой у злоумышленника есть возможность внести изменения в страницу и доставить такую страницу пользователю. Типичный сценарий: злоумышленник посылает жертве ссылку, параметры которой модифицируют страницу, которую жертва откроет, перейдя по ссылке. Жертва при этом видит как бы доверенный домен.
Darth_Anjan
Оставлю полезную ссылку по теме (можно по репе походить — там много интересного лежит).