Перевод статьи подготовлен специально для студентов курса «Реверс-инжиниринг».
Универсальный XSS (uXSS) – это баг браузера, который дает возможность выполнять код на JavaScript на любом сайте.
Кажется, будто XSS есть на всех сайтах и выглядит это очень интересно. Что еще интереснее, так это то, как я нашел эту ошибку. Обычно, если речь заходит о uXSS, то это скорее всего связано с элементом IFRAME или возней с URL, но я никогда не думал, что найду XSS-уязвимость, используя функцию
Давайте поговорим о том, что на самом деле происходит, когда Edge отображает окно предварительного просмотра печати.
Я всегда думал, что там находится просто скриншот, отрисованный технологией типа Canvas, но на самом деле страница, которую вы собираетесь печатать, копируется в temp и повторно рендерится!
Когда на странице выполняется
Итак, файл создается во временной директории Edge, и содержимое этого файла представляет собой слегка измененную версию исходной страницы, которую мы пытались распечатать. Давайте сравним.
Перед печатью:
После печати:
Есть несколько вещей, которые мы можем заметить из этого сравнения.
В отношении первого и второго пункта я провел несколько тестов. Сначала я хотел понять, могу ли я получить валидный Javascript-код после смены кодировки, в надежде, что в итоге я смогу выполнить Javascript. Оказалось, что любой код внутри элемента
Универсальный XSS (uXSS) – это баг браузера, который дает возможность выполнять код на JavaScript на любом сайте.
Кажется, будто XSS есть на всех сайтах и выглядит это очень интересно. Что еще интереснее, так это то, как я нашел эту ошибку. Обычно, если речь заходит о uXSS, то это скорее всего связано с элементом IFRAME или возней с URL, но я никогда не думал, что найду XSS-уязвимость, используя функцию
print()
.Окно предварительного просмотра
Давайте поговорим о том, что на самом деле происходит, когда Edge отображает окно предварительного просмотра печати.
Я всегда думал, что там находится просто скриншот, отрисованный технологией типа Canvas, но на самом деле страница, которую вы собираетесь печатать, копируется в temp и повторно рендерится!
Когда на странице выполняется
print()
, мы видим следующую активность файловой системы в Process Monitor: Итак, файл создается во временной директории Edge, и содержимое этого файла представляет собой слегка измененную версию исходной страницы, которую мы пытались распечатать. Давайте сравним.
Перед печатью:
Printer Button Print!
После печати:
Printer ButtonPrint!
Есть несколько вещей, которые мы можем заметить из этого сравнения.
- Javascript кодируется и рендерится неправильно.
- Теперь IFRAME указывает на другой локальный файл в той же самой директории, которая содержит исходный код оригинальной ссылки
bing.com
. - У HTML элемента теперь есть своеобразный атрибут
__IE_DisplayURL
.
В отношении первого и второго пункта я провел несколько тестов. Сначала я хотел понять, могу ли я получить валидный Javascript-код после смены кодировки, в надежде, что в итоге я смогу выполнить Javascript. Оказалось, что любой код внутри элемента
script>
, нормальный или нет, выполняться не будет.
Второй пункт помог мне раскрыть имя пользователя операционной системы с помощью функционала @media print{}
в CSS и магии селекторов. Я смог получить его из значения IFRAME href. Однако и этого было недостаточно.
Вот на третьем пункте стало интересно, поскольку этот атрибут крайне необычный и до этого момента я с ним не встречался. Я сразу же его загуглил и нашел несколько статей, исходя из которых понял, что некто Масато Кинугава уже поиграл с ним и обнаружил крутые баги.
После чтения и некоторой практики, я обнаружил, что контекст предварительного просмотра узнает из этого атрибута откуда появляется документ. Это имеет смысл, поскольку Edge открывает файлы с помощью file:
URI схемы. С помощью этого атрибута, указывающего на источник, вы заметите, что все запросы, поступающие от документа (в рамках предварительного просмотра), будут имитировать точно такое же поведение, как если бы они поступали с исходного веб-сайта.
Как мы можем использовать этот атрибут? Должен же быть какой-то способ!
Выполнение кода на Javascript с помощью предварительного просмотра
Как я уже говорил, любой код на JavaScript, находящийся в нормальном теге script будет заблокирован или просто проигнорирован. Но что если мыслить в другом направлении? Я перепробовал все, что только мог придумать, поэтому избавлю вас от траты времени на множество неудачных попыток и перейду сразу к делу.
Здесь мы имеем дело с функцией печати, поэтому я играл с событиями, относящимися к печати. Результат мне принесло "onbeforeprint"
, с помощью него я получил возможность внедрять IFRAME, который указывал на любой веб-сайт и Edge не нужно было конвертировать его сначала в файл. Почти сразу я попытался внедрить IFRAME, который указывал на URL-адрес Javascript-кода и та-дам! Этот код выполнялся в контексте предварительного просмотра.
Тест Javascript-инъекции:
Printer Button Print!
После конвертации предварительного просмотра документа:
Printer ButtonPrint!
Скриншот результата:
То, что мы умеем выполнять код еще не значит, что мы закончили. Как я уже говорил ранее, из-за атрибута __IE_DisplayURL
любой запрос или вызов API будут рассматриваться, как исходящие от документа.
Реализация uXSS
Теперь, когда мы умеем внедрять свой исполняемый код, нам нужно каким-то образом создать свой собственный «предварительный просмотр документа» с нашим собственным __IE_DisplayURL
, а затем мы сможем сымитировать любой веб-сайт, который выберем для uXSS.
Я обнаружил, что с помощью Blob URL я смогу добиться нужного эффекта! Поэтому я сделал свой собственный документ для печати со своим атрибутом, указывающим на целевой сайт (в моем случае bing.com
). Он содержал Javascript IFRAME, который выполнялся, как будто он исходит от bing.com
.
Я внедрил следующий код:
if (top.location.protocol == 'file:') { setTimeout(function() { top.location = URL.createObjectURL(new Blob([top.document.getElementById('qd').value], { type: 'text/html' })) }, 1000) }
Где top.document.getElementById('qd').value
— это следующий фейковый документ для печати.
Все, что я делаю – это читаю document.cookie
и отправляю их обратно серверу.
А теперь обобщим то, что делает финальная версия эксплойта:
- Используя событие
onbeforeprint
, я внедряю IFRAME, который указывает на мою полезную нагрузку непосредственно перед печатью. - Для инициализации я вызываю
window.print()
. - Затем Edge отображает окно предварительного просмотра во время рендеринга моего внедренного кода;
- Внедренный Javascript-код создал Blob URL-адрес, который содержит мой собственный документ для печати
bing.com
и перенаправляет верхний фрейм на этот адрес. - Контекст предварительного просмотра печати думает, что содержимое моего Blob URL-адреса – настоящий документ для печати и устанавливает происхождение документа как
bing.com
с помощью атрибута __IE_DisplayURL
. - Фейковый документ для печати сам по себе содержит другой IFRAME, который просто отображает
document.cookie
источника bing.com
. - uXSS работает!
Итоговый код и видео
Полезные ссылки:
→ Microsoft.com