Привет, Хабр! Сегодня мы познакомимся с DOM XSS и Prototype Pollution, рассмотрим примеры уязвимостей на Bug Bounty и научимся использовать инструмент DOM Invader, который заметно упростит поиск таких уязвимостей.

Материал будет интересен специалистам, которые уже сталкивались с DOM XSS и Prototype Pollution. Если вы еще не знакомы с этими уязвимостями, стоит обратить внимание на лабораторные PortSwigger и теорию и только потом приступать к изучению DOM Invader. А если знакомы, то быстро вспомним основные понятия и объясним, а в чем же, собственно, состоит опасность.

XSS

Межсайтовый скриптинг, известный как XSS, часто встречается в мире веб-безопасности. Наряду с IDOR он занимает первые места в рейтинге Bug Bounty по количеству найденных уязвимостей. Атака позволяет внедрить JavaScript-код на страницу приложения, после чего пользователи, посещающие уязвимую страницу, могут потерять различные конфиденциальные данные — например, сеансовые куки или токены авторизации.

Document Object Model (DOM)

Document Object Model (DOM) — это иерархическое представление элементов на странице в веб-браузере. Сайты могут использовать JavaScript для управления узлами и объектами DOM. Манипуляции с DOM сами по себе не являются проблемой — так работают все современные сайты. А вот JavaScript, который небезопасно обрабатывает данные, может позволить провести различные атаки. Уязвимости DOM возникают, когда на сайте содержится JavaScript, который берет значение, контролируемое пользователем (Source), и передает его в опасную функцию (Sink).

Source

Свойство JavaScript, которое принимает данные, потенциально контролируемые пользователем. Пример источника — свойство location.search, поскольку оно считывает ввод из строки запроса, которой относительно просто управлять. В конечном итоге любое свойство, которым может управлять пользователь, является потенциальным Source. К этому относятся URL-адрес источника (document.referrer), Cookie пользователя (document.cookie) и WebMessages (подробнее про WebMessages написано здесь).

Sink

Потенциально опасная функция JavaScript или объект DOM, которые могут вызвать уязвимость, если в них передаются данные, контролируемые пользователем. Например, функция eval() является Sink'ом, поскольку она обрабатывает аргумент, который в него передается, как JavaScript. Примером HTML-Sink является document.body.innerHTML, так как это потенциально позволяет внедрить HTML и выполнить произвольный JavaScript.

Gadget

Небольшие фрагменты кода, которые могут быть использованы для эксплуатации уязвимостей. «Гаджеты» часто применяются в цепочках уязвимостей для достижения более значительного импакта. Еще их используют для обхода защитных мер, повышения привилегий или выполнения произвольного кода.

DOM Invader

DOM Invader — это браузерный инструмент, который поможет в поиске уязвимостей DOM XSS, Prototype Pollution и DOM Clobbering. Суть его функции — в изучении различных Sources и Sinks, включая векторы postMessage. Он доступен через встроенный браузер Burp Suite: это уже предустановленное расширение, его нужно просто включить в настройках.

Что умеет DOM Invader?

  1. Проверять на DOM XSS. Расширенное представление DOM позволяет мгновенно определить управляемые Sinks на странице, показывая вам точку внедрения и XSS-контекст, и то, как обрабатывается переданный ввод.

  2. Логировать, изменять и повторно отправлять WebMessage с помощью метода postMessage(). Это позволяет тестировать DOM XSS через WebMessage (подробнее).

  3. Автоматически определять Sources Prototype Pollution на стороне клиента и сканировать управляемые Gadgets, которые передаются в опасные Sinks.

  4. Автоматически выявлять уязвимости DOM clobbering (подробнее).

Прежде чем начать использовать инструмент, важно вспомнить сам контекст возникновения уязвимостей DOM XSS, так что нам пригодится немного практики.

Подготовка

1. Обновите Canary (ключевое слово, используемое для идентификации уязвимости), в нашем случае это XXS.

2. Откройте вкладку DOM Invader в инструментах разработчика (F12).

3. Откройте сайт, который будете проверять на уязвимости, и внедрите Canary в параметры.

4. На вкладке DOM Invader проверьте, возвращается ли Canary в Sink.

5. При необходимости проверьте Stack Trace. Он отображается в консоли при нажатии на соответствующую кнопку в DOM Invader.

А теперь перейдем к практике и разберемся с работой расширения на примере некоторых лабораторных Академии PortSwigger.

Лабораторная 1. DOM XSS in document.write sink using source location.search

Здесь мы можем использовать функцию Inject forms, а затем нажать «Поиск» на странице сайта:

В расширении можно будет увидеть, что Sink найден. Теперь можно пробовать проэксплуатировать уязвимость:

Чтобы изучить эту точку внедрения подробнее, мы можем открыть Stack Trace, после чего получим уведомление о том, что трассировка доступна в консоли:

Теперь переходим в консоль и открываем кусок кода, в котором обнаружен Sink:

<script>
function trackSearch(query) {document.write('<img src="/resources/images/track
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
trackSearch(query);
}
</script>

Сначала мы создаем переменную query и добавляем в нее значение параметра search:

var query = (new URLSearchParams(window.location.search)).get('search');

Если query не null, мы вызываем функцию trackSearch и передаем в нее значение:

if(query) {
trackSearch(query);
}

Для записи строк на страницу используется функция document.write(). Сначала загружается страница, а затем на нее добавляется наше значение query:

document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">')

В данном случае Source, или точка внедрения, — это значение location.search, которое мы контролируем, а Sink'ом является document.write.

Теперь переходим в DOM Invader и пробуем использовать кнопку Exploit — быть может, полезная нагрузка, подобранная расширением, сработает?

Эксплойт не сработал, но можно заметить, что мы добавили на страницу "> и застряли в HTML-теге:

Пробуем воспользоваться полезной нагрузкой, которая отобразилась на странице, и добавляем значение, которое отобразилось вне тега с результатом поиска:

“><img src=x onerror=alert(1)>

Хоть DOM Invader и не подсказал нам точный способ эксплуатации, данный инструмент помогает в поиске Sources, которые впоследствии можно эксплуатировать вручную.

Это довольно простой пример, и такую XSS можно найти вручную. Но когда мы тестируем приложение с огромным количеством форм, ручной анализ займет много времени, а DOM Invader ускорит нашу работу. В таком формате тестирование становится эффективнее. Нам больше не нужно тратить часы на изучение функций, источников и тому подобного, чтобы найти XSS.

Лабораторная 2. DOM XSS in innerHTML sink using source location.search

Переходим в DOM Invader, жмем inject forms, после нажимаем поиск на странице:

Смотрим Stack Trace:

document.getElementById('searchMessage').innerHTML = query;

Эта строка вставляет наши входные данные в элемент HTML с идентификатором “searchMessage”. Здесь используется метод .innerHTML, что создает потенциальную уязвимость, так как он вставляет небезопасные данные напрямую в элемент.

Жмем Exploit в DOM Invader и видим, что в результате вывелось оповещение и JavaScript выполнился:

Лабораторная 3. DOM XSS in document.write sink using source location.search inside a select element

В данной лабораторной немного меняются точка внедрения и логика приложения, поэтому, чтобы найти точку внедрения, необходимо проверить функцию проверки остатков товара:

Посмотрев запрос, находим параметр storeId в функции проверки остатков товара:

Передаем параметр storeId в GET-запросе и запускаем DOM Invader, жмем Inject URL params:

Смотрим код, в котором возникает уязвимость.

В этом фрагменте кода сначала присваивается значение переменной store параметра storeId из URL страницы:

var store = (new URLSearchParams(window.location.search)).get('storeId');

Значение 'storeId' не проверяется и попадает на страницу без обратки.

document.write('<select name="storeId">"'></select><img src onerror=alert(1)>');

Данная строчка кода отвечает за создание элемента выпадающего списка, и небезопасная функция document.write() создает уязвимость.

if(store)
{
document.write('<option selected>'+store+'</option>');
}

Существует условие: если для store задано значение, оно добавляется в список и отмечается как выбранное. Здесь и возникает уязвимость DOM XSS, так как значение store не проходит должной обработки.

Жмем Exploit и видим, что XSS не сработала и наш ввод попал между тегов option:

Чтобы уязвимость сработала, возвращаемся к коду, смотрим на уязвимую функцию и немного изменяем эксплойт, чтобы закрыть тег select. Наша полезная нагрузка попадет в Sink примерно так:

document.write('<select name="storeId">'</select><img src onerror=alert(1)>');

Жмем Exploit в DOM Invader и видим, что в результате вывелось оповещение:

Лабораторная 4. DOM XSS via client-side prototype pollution

Теперь мы ищем Prototype Pollution, и нам необходимо включить режим для поиска этой уязвимости в расширении.

Переходим на главную страницу и открываем расширение. Оно сразу обнаружило уязвимость:

Жмем Scan Gadgets и видим найденный Sink:

Смотрим Stack Trace в консоли браузера и переходим к коду, где возникает уязвимость:

Уязвимая часть кода:

if(config.transport_url) {
let script = document.createElement('script');
script.src = config.transport_url;
document.body.appendChild(script);
}

В этом участке кода перед использованием config.transport_url не проверяется его источник. В результате, добавив proto[transport_url]=data:,alert(1) в запрос, можно заставить каждый объект, прошедший через deparam, получать свойство transport_url со значением data:,alert(1). Это значение будет использоваться как источник для нового элемента script, что приведет к выполнению кода alert(1).

Нажимаем Exploit:

Лабораторная 5. Client-side prototype pollution via browser APIs

Переходим в DOM Invader, где можно увидеть, что найдено две точки внедрения:

В данном случае расширение не находит уязвимый участок кода, поэтому используем сканирование на гаджеты:

Теперь у нас есть источник и точка внедрения, переходим в DOM Invader, смотрим Stack Trace и уязвимый код:

Вы можете самостоятельно разобрать данный код, это будет хорошей практикой. Воспользуемся кнопкой Exploit и видим, что полезная нагрузка для исполнения JavaScript сработала:

Bug Bounty. DOM XSS in Habr.com

Можно подумать, что расширение поможет только при поиске простых XSS в лабораторных и не особо применимо на Bug Bounty или в новом проекте, но мой опыт показывает, что DOM Invader способен находить уязвимость где угодно.

Поскольку я публикую текст на Habr.com, я решил протестировать его на уязвимость и доказать вам тезис об универсальности DOM Invader.

Detect

Добавляем # на страницу и нашу канарейку после якоря, смотрим вкладку DOM Invader:

Жмем Exploit:

Impact

Начнем с того, что мы можем реализовать перенаправление пользователя с помощью простой полезной нагрузки.

#'><img src onerror=eval(atob("ZG9jdW1lbnQubG9jYXRpb249aHR0cHM6Ly9ldmlsLmNvbQo="))>

Где «ZG9jdW1lbnQubG9jYXRpb249aHR0cHM6Ly9ldmlsLmNvbQo=»: document.location=https://evil.com

 Хм, а чем это опасно?

  • Кража email

Перенаправление — это самый простой вектор атаки, и он может сыграть важную роль в эксплуатации данной уязвимости.

Для того чтобы увеличить импакт от уязвимости, я определил критичные пользовательские данные на Habr и заметил, что email авторизованного пользователя лежит в ответе от страницы. Оставалось только захватить данный элемент. Чтобы это сделать, я написал небольшой скрипт, который будет искать на странице элемент email с помощью регулярного выражения и отправлять его нам на сервер.

В результате мне удалось добиться кражи email авторизованного пользователя:

  • Phishing XSS

В качестве эксперимента я реализовал фишинговый вектор атаки с отрисовкой формы логина в аккаунт, полезная нагрузка помещается в eval:

Внимательный читатель может сказать, что размер полезной нагрузки может быть достаточно большим, чтобы передавать его пользователю, на что я напомню про перенаправление, которое может помочь при эксплуатации уязвимости. Таким образом, пользователю нужно будет передать страницу с перенаправлением на уязвимый сайт, который после отправит его обратно на Habr, добавив якорь с полезной нагрузкой :)

О найденной уязвимости я сообщил команде Habr, они отреагировали в течение одного часа, после чего уязвимость была успешно устранена, а я получил свое Bounty. Отдельное спасибо за быструю реакцию и разрешение упомянуть уязвимость в статье!

Полезные статьи в заключение

Мы познакомились подробнее с природой уязвимостей DOM XSS и Prototype Pollution, узнали, как пользоваться DOM Invader и для чего он нужен. Надеюсь, статья была для вас полезной, и советую дополнительно почитать вот такие материалы:

Никита Чистяков

Консультант по информационной безопасности, «Инфосистемы Джет»

Комментарии (0)