Хакер - DOM XSS через Web Messaging. Как работает легкий способ получить XSS с помощью postMessage
hacker_frei
W0lFreaK
Содержание статьи
- Что такое DOM?
- Как эксплуатируются DOM-based XSS?
- Механизмы безопасности кросс-доменного взаимодействия
- Web Messaging API
- DOM-based XSS через Web Messaging
- Origin Verification отсутствует
- Origin verification
- JSON Parse
- Выводы
Недавно я обнаружил новую для себя разновидность XSS-уязвимостей — DOM XSS с помощью Web Messaging. Перечитав множество страниц документации, я решил создать единый материал по эксплуатации XSS через эту технологию. Я опишу здесь наиболее распространенные недостатки безопасности и методы их эксплуатации.
ЧТО ТАКОЕ DOM?
DOM (Document Object Model) — не зависящий от платформы и языка программный интерфейс, который позволяет программам и скриптам получать доступ к содержимому HTML-, XHTML-, XML-документов, а также изменять их контент (содержимое, структуру, оформление). DOM-based-уязвимости возникают, когда веб‑сайт содержит сценарий, который принимает контролируемое злоумышленником значение и передает его небезопасной функции, называемой sink. Один из видов DOM-based-уязвимостей — DOM-based XSS. Уязвимости этого типа возникают, когда JavaScript получает данные от пользователя и передает их в sink, обладающий возможностью динамического исполнения кода, например eval(), document.write() или innerHTML.
КАК ЭКСПЛУАТИРУЮТСЯ DOM-BASED XSS?
Наиболее популярный источник DOM XSS — URL-страницы. Доступ к этому значению осуществляется с помощью JavaScript через объект window.location. Затем URL обрабатывается внутри существующего на странице легитимного скрипта. В этом случае атакующий может создать ссылку с вредоносной нагрузкой, чтобы затем отправить ее жертве. Когда жертва перейдет по ссылке, исходный скрипт на странице, использующий объект window.location, запросит адрес текущей страницы и исполнит нагрузку, которая содержится в URL. Вот простейший пример этой уязвимости.
<body>
<script>document.write(location.href);</script>
</body>
При получении такой страницы браузер автоматически выполнит скрипт и запишет в тело страницы (document.write) строку, взяв ее значение из location.href (полного адреса страницы). Однако пользователь контролирует значение location.href и может поместить в него произвольную строку. Поэтому для эксплуатации XSS-уязвимости достаточно сформировать следующую ссылку, и при ее открытии в браузере выполнится вредоносный скрипт.
http://website.com/index.html#<script>alert(1)</script>
МЕХАНИЗМЫ БЕЗОПАСНОСТИ КРОСС-ДОМЕННОГО ВЗАИМОДЕЙСТВИЯ
Представим, что у нас есть страница сайта, который мы разработали. Мы помещаем на нее элемент <iframe> и указываем в качестве его источника произвольный сайт.

Если бы не было механизмов безопасности, обмен данными между элементами родительской веб‑страницы и веб‑страницы, которая отображается в <iframe>, мог бы быть критически опасным для пользовательских данных. Любой скрипт, находящийся на родительской странице, мог бы получить доступ к любым данным, размещенным внутри <iframe>.
Давай рассмотрим на примере. Злоумышленник помещает на своей странице <iframe> с адресом интернет‑банка, в котором пользователь был авторизован ранее. Затем каким‑то образом заманивает жертву на свою страницу. В результате злоумышленник может получить доступ к любым пользовательским данным личного кабинета интернет‑банка жертвы. Чтобы не допустить подобного, были созданы два механизма защиты:
- Same Origin Policy (SOP), «политика одинакового источника», — предотвращает кросс‑доменные атаки, блокируя чтение загружаемых ресурсов из другого источника. Источник идентифицируется по следующей тройке параметров: схема, полное имя хоста и порт. Когда хотя бы один из параметров у источников не совпадает, обмен данными между ресурсами запрещается. Например, если страница по адресу
http://example.com/index.htmlпопробует отобразить данные из источникаhttps://example.com/, то это действие будет запрещено, так как у источников не совпадает протокол.

2. Cross-Origin Resource Sharing. Ограничения политики одинаковых источников оказались слишком жесткими. Поэтому для более тонкой настройки доступа к ресурсам создали «механизм совместного использования ресурсов разными источниками» — CORS. Он регламентирует три категории сетевого взаимодействия:
- запись из разных источников. Эта категория регламентирует переадресации, отправки форм и переходы по ссылкам. По умолчанию все эти действия разрешены;
- вставка из разных источников. Эта категория регламентирует элементы, загружаемые посредством тегов
<link>,<script>,<img>,<video>,<audio>,<iframe>и других. По умолчанию все они разрешены, однако работоспособность тега<iframe>может быть дополнительно ограничена с помощью заголовкаX-Frame-Options; - считывание из разных источников. Эта категория регламентирует элементы, загружаемые через AJAX и fetch. По умолчанию эти возможности заблокированы.
WWW
Подробнее о CORS и SOP ты можешь прочитать, к примеру, в этих статьях на «Хабрахабре»: «CORS для чайников: история возникновения, как устроен и оптимальные методы работы» и «Политика общего происхождения и CORS: визуальное руководство».
WEB MESSAGING API
Window.postMessage() — это метод, позволяющий передавать данные между документами, которые загружены в разных окнах или фреймах, в том числе между документами, полученными с разных доменов. Если на сайте корректно настроены механизмы безопасности (SOP и CORS), использование postMessage будет единственным доступным способом передачи данных между документами на разных доменах. Запросы, созданные с помощью прочих методов (как, например, XMLHttpRequest или Fetch API), будут заблокированы в соответствии с SOP и CORS.
Поиск информации о технологии postMessages привел меня к официальной документации Mozilla. Обычно сценариям из разных источников разрешен доступ друг к другу тогда и только тогда, когда они соответствуют Same Origin Policy (одинаковая схема, имя хоста и порт), включая сценарии внутри фрейма, которые обращаются к родителю фрейма. Window.postMessage() предоставляет контролируемый механизм для безопасного обхода этого ограничения. Также я нашел в документации способы обеспечения связи между родительской страницей и страницей внутри фрейма. В общем виде дочерний <iframe> должен быть подписан на событие «сообщение»:
window.addEventListener("message", (event) => {...}, false);
Здесь message — ожидаемое сообщение.
В таком случае родительская страница может передать дочернему фрейму сообщение с помощью такого метода:
postMessage(message, targetOrigin, transfer)
Здесь message — отправляемое сообщение, эти данные автоматически сериализуются для передачи в дочерний фрейм, а targetOrigin указывает источник родительского окна.
В качестве targetOrigin допускается использовать звездочку, которая указывает на то, что получить сообщение может кто угодно. Либо можно указать конкретный URI, который будет проверен внутри слушателя в дочернем фрейме. Если Origin страницы не совпадает с targetOrigin внутри этой функции, событие не будет отправлено. Этот механизм обеспечивает контроль над тем, куда отправляются сообщения. Например, если postMessage используется для отправки пароля, необходимо, чтобы этот аргумент соответствовал целевому URI. Это позволит предотвратить хищение пароля злоумышленником через недоверенный ресурс.
DOM-BASED XSS ЧЕРЕЗ WEB MESSAGING
Давай посмотрим, как можно использовать веб‑сообщения в качестве источника для создания и эксплуатации DOM XSS на целевой странице. Если целевая страница обрабатывает входящие сообщения небезопасным образом (например, неверно проверяя источник входящих сообщений), то вызываемые слушателем события потенциально могут стать приемниками небезопасной нагрузки и источником XSS.
Например, злоумышленник может поместить на своей странице вредоносный <iframe> и использовать метод postMessage() для передачи данных с помощью веб‑сообщения уязвимому слушателю событий. В дальнейшем слушатель передаст вредоносную нагрузку в приемник на дочерней странице.
Это значит, что веб‑сообщения могут быть использованы в качестве источника нагрузки для любого из приемников дочерней веб‑страницы. Результат эксплуатации уязвимости зависит от того, как целевой документ обрабатывает полученные сообщения.
Если целевая страница полностью доверяет отправителю, не проверяет данные, полученные от него, и без искажений передает их в приемник, то эта уязвимость позволит злоумышленнику совершить любые действия от лица пользователя в контексте целевого ресурса (скомпрометировать пользователя).
Давай рассмотрим разные варианты уязвимого кода.
Origin Verification отсутствует
Целевая страница легитимного сайта содержит следующий скрипт.
<script>
window.addEventListener('message', function(e){
document.getElementById('qwe').innerHTML = e.data;
})
</script>
В этом случае эксплуатация уязвимости возможна с помощью <iframe>, помещенного на сервере атакующего, примерно с таким содержимым:
<iframe src="http://target.com/" onload="this.contentWindow.postMessage('<img src=1 onerror=alert`1`>','*')">
Как видишь, слушатель сообщений не проверяет источник, а метод postMessage указывает targetOrigin «звездочка». Процесс эксплуатации будет выглядеть так:
postMessage()в пейлоаде создаст сообщение, которое отправится странице в<iframe>после того, как загрузится содержимое<iframe>;EventListenerцелевой страницы получит сообщение с атакующей страницы;EventListenerвыполнит поиск элемента с IDqwe;EventListenerвыполнит вставку полученного сообщения в найденный тег<div>;- в итоге в целевую страницу будет вставлен тег
img, содержащий некорректный адрес источника. Это вызовет ошибку, а обработка этой ошибки исполнит произвольный код, указанный атакующим, то есть вызоветalert(1).
Origin verification
Представим, что целевая страница легитимного сайта содержит такой скрипт:
window.addEventListener('message', function(e) {
if (e.origin.indexOf('normal-website.com') > -1) {
eval(e.data);
}
});
В том случае, если слушатель проверяет Origin, могут найтись проблемы в реализации этих проверок. Например, проверку с помощью метода indexOf можно обойти, если сделать вредоносный скрипт на многоуровневом домене, содержащем в себе искомую подстроку. Например, таком:
<http://www.normal-website.com.attacker.com>
Аналогичным образом обходятся проверки, которые выполняются посредством методов startsWith(), endsWith() и других.
JSON Parse
Нередки случаи, когда слушатель может выполнить одно из нескольких заранее указанных действий, в зависимости от того, какое именно сообщение он получит при выполнении postMessage. Подразумевается, что дочерний фрейм сможет по‑разному реагировать на различные сообщения, приходящие из родительского элемента. Эта вариативность действий будет описана в дочернем <iframe> подобным скриптом:
<script>
window.addEventListener('message', function(e) {
var iframe = document.createElement('iframe'), ACMEplayer = {element: iframe}, d;
document.body.appendChild(iframe);
try {
d = JSON.parse(e.data);
} catch(e) {
return;
}
switch(d.type) {
case "img":
ACMEplayer.element.src = d.url;
break;
case "redirect":
window.location.replace(d.redirectUrl);
break;
}
}, false);
</script>
В данном случае в зависимости от сообщения, получаемого дочерним iframe, он может выполнить одно из двух действий:
- Если в результате вызова postMessage в дочерний
iframeпередано сообщение, содержащее{"type" : "img", "url" : "https://domain.com"}, дочернийiframeприсвоит указанный URL свойствуsrcэлементаACMEplayerотображаемой в нем страницы. - Если в результате вызова postMessage в дочерний
iframeпередано сообщение, содержащее{"type" : "redirect", "url" : "https://domain.com"}, то дочернийiframeвыполнит переход с текущей страницы на указанный URL.
Для эксплуатации уязвимости необходимо сформировать корректный JSON-объект, который будет передан в дочерний фрейм и правильно обработан внутренним элементом case. Пример вредоносной нагрузки:
<iframe src=https://victim.com/ onload='this.contentWindow.postMessage("{"type":"img","url":"javascript:alert(1)"}","*")'>
В результате отправки такого сообщения у элемента ACMEplayer в src будет установлено значение, соответствующее URL из нашей вредоносной нагрузки. Поскольку второй аргумент указывает, что любой targetOrigin разрешен для postMessage, а обработчик событий не содержит какой‑либо формы проверки источника, полезная нагрузка будет установлена и исполнена, когда жертва перейдет на эту страницу.
ВЫВОДЫ
Мы подробно разобрали уязвимости, которые возникают при неаккуратном обращении с Web Messaging API для организации передачи данных между родительской страницей и загружаемой внутри <iframe>. Вот несколько рекомендаций, которые позволят избежать подобных проблем на своем сайте:
- проверяй оригинальность источника сообщения, чтобы предотвратить внедрение конструкций с недоверенных сайтов;
- используй безопасные способы проверки подлинности оригинального источника сообщения;
- проверяй сообщения на наличие потенциально опасных содержаний и команд перед их выполнением;
- используй механизмы шифрования для защиты конфиденциальности сообщений во время передачи.
Ну а если ты пентестер, а не веб‑дизайнер, то не забудь проверить эти техники, когда встретишь веб‑сообщения.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei