• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Написание обхода DOMPurify 2.0.0 с использованием мутации XSS

tabac

CPU register
Пользователь
Регистрация
30.09.2018
Сообщения
1 610
Решения
1
Реакции
3 332
Вчера была выпущена новая версия DOMPurify (очень популярной библиотеки очистки XSS), которая исправила обход, о котором мы сообщали. В этом посте я покажу, как именно выглядел обход, которому предшествует общая информация о DOMPurify и как он работает. Если вы знаете, как работают очистители и что такое mXSS - вы можете сразу перейти к пункту mXSS в Chromium (и Safari) .

HTML sanitizers – зачем они нужны и как они работают

Довольно распространенный вариант использования в веб-приложениях заключается в том, что пользователям разрешается вводить некоторые HTML-данные, в основном в форме полнофункциональных редакторов, что позволяет включать форматирование в текст (например, жирный , курсив и т.д.). Эта функция обычно возможна в веб-почте, блог-платформах и т.д. Основная проблема заключается в том, что пользователь может включить вредоносный код HTML/JavaScript и внедрить XSS.

Именно здесь в игру вступают HTML-sanitizers или по-русски "Очистители XSS". Их главная цель - взять ненадежный ввод, очистить его и создать безопасный HTML (HTML с удаленными опасными тегами).

Untitled-Diagram-1.png

Идея HTML-sanitizers

HTML-sanitizers обычно выполняют очистку путем анализа входных данных (есть несколько способов сделать это из JavaScript, один из примеров - использование DOMParser.prototype.parseFromString метода). Затем HTML-sanitizers получают список разрешенных элементов и атрибутов, обходят дерево DOM и удаляют все, чего нет в списке (это немного упрощено, потому что реальные sanitizers зачастую более сложны, чем это, но для примера это довольно).

Итак, давайте предположим, что у нас есть HTML-sanitizer со следующим списком разрешений:
  • Элементы: <div>, <b>, <i> и <img>.
  • Атрибут: только src.
и пользователь вводит следующий HTML:

HTML:
<div>I am trying to be <i>malicious</i> <u>here</u>! <img src=1 onerror=alert(1)></div>

После парсинга мы получим следующее дерево DOM:
image.png

Небезопасный HTML

Итак, есть две вещи, которые следует удалить:
  1. <u> элемент, не находящийся в allow-списке,
  2. onerror атрибут, не находящийся в allow-списке.
Поэтому после обхода дерева DOM очиститель должен оставить только следующее:
image-1.png

Безопасный, очищенный HTML

Теперь у нас есть «безопасное» дерево DOM со всеми недопустимыми элементами или атрибутами. Следовательно, очиститель выдаст следующую строку после выполнения очистки:
HTML:
<div>I am trying to be <i>malicious</i> here! <img src="1"></div>

Теперь это безопасный HTML-фрагмент, который можно без опасений вставлять в наше DOM-дерево, верно? Это верно, но с одной важной оговоркой: так называемая мутация XSS.


Что такое мутация XSS?

Основным источником изучения мутации XSS (mXSS) по-прежнему является статья Марио Хайдериха, написанная в 2013 году, которая называется mXSS Attacks: Атака хорошо защищенных веб-приложений с использованием мутаций innerHTML. В следующих параграфах я дам вам краткий обзор того, что такое mXSS и почему необходимо было обойти DOMPurify.

innerHTML это очень удобный метод в элементах DOM, с помощью которого мы можем просто ввести некоторый HTML, и он автоматически анализируется и вставляется в дерево DOM. Например, если мы делаем следующий код:
HTML:
element.innerHTML = '<u>Some <i>HTML'
Правая часть назначения автоматически анализируется и вставляется в дерево DOM как чайлд-элементы element. Дело в том, что innerHTML означает, что браузер может изменять строку, которую мы хотели вставить. Например, если я попытаюсь прочитать element.innerHTML выше, я получу следующий результат:

image-2.png

Написание и чтение из innerHTML

Как видите, сразу после записи innerHTML, значение, которое я получаю, отличается. Это ожидаемо. В конце концов, пользователь может ввести неработающий HTML, и браузер должен это исправить.

Но мы открываем ящик Пандоры, когда понимаем, что иногда вход может изменяться несколько раз. Предположим, у нас есть следующее выражение:
HTML:
element.innerHTML = element.innerHTML
На первый взгляд, назначение innerHTML не должно иметь значения. Но дело в том, что из-за ошибок в браузерах иногда это происходит. И это как раз тогда, когда происходит MXSS.


mXSS в Chromium

Так что обход DOMPurify может произойти, потому что я нашел новый вектор mXSS в текущей версии Chrome (77). Давайте начнем с примера:

image-3.png

Попытка поместить <p> в <svg>

Я присваиваю тег <svg>тегу <p> (являющийся дочерним элементом <svg>). Однако, как вы можете видеть в дереве DOM, <p> элемент фактически «выпрыгнул» из <svg>. Это произошло потому, что это недопустимый тег внутри <svg>, поэтому браузер решил закрыть его и открыть <p> после него.

Но давайте посмотрим, что произойдет, когда я попытаюсь поместить закрывающий </p> тег в SVG:

image-4.png

Попытка поместить </p> в <svg>

В, возможно, удивительном повороте событий, <p> элемент теперь является потомком <svg>. Кроме того, как вы можете видеть внизу, Chrome автоматически добавил открывающий <p> тег. Что означает, что если я попытаюсь присвоить innerHTML себе, он будет мутировать!

image-5.png

mXSS в Chrome

Таким образом, полезная нагрузка <svg></p> является базой для mXSS, потому что она мутирует при назначении innerHTML; контент, который изначально находится внутри <svg>, выпрыгивает из него. Остается вопрос, как его использовать.


Злоупотребление mXSS для обхода DOMPurify

Попробуем присвоить следующую строку innerHTML элементу DOM:
HTML:
<svg></p><style><a id="</style><img src=1 onerror=alert(1)>">

image-6.png

В этом фрагменте DOM нет ничего плохого по своей сути. Все теги ( <div>, <svg>, <p>, <style> и <a>) и атрибут id разрешены DOMPurify в дефолтной конфигурации. Так что, это ничего не меняет в этом коде. Однако, когда мы пытаемся присвоить innerHTML ему же ...

image-7.png

… внезапно появился alert!

Здесь происходит злоупотребление определенным поведением <svg> элемента. По сути, когда вы открываете <svg> в своем HTML-коде, правила синтаксического анализа браузера меняются и становятся ближе к синтаксическому анализу XML, чем к синтаксическому анализу HTML. Одно из основных отличий заключается в том, что определенные теги в HTML не могут иметь "детей" при десериализации из текста. Примером является <style>. Если вы посмотрите на спецификацию HTML, вы обнаружите, что ее модель контента - это Text. Даже если вы попытаетесь поместить элемент в a <style>, он будет рассматриваться как текст:

image-9.png

То же самое не относится к SVG. Давайте попробуем точно такой же пример, но с <style> ребенком <svg>:

image-10.png

Как видите, теперь у <style> есть дочерний элемент.

Итак, теперь давайте посмотрим пример с DOMPurify:

image-14.png


В этом случае браузер предполагает, что оба </p> и <style> являются дочерними элементами тега <svg>, в результате чего <a> элемент является дочерним элементом <style>. Тем не менее, код немного видоизменяется и теперь есть также открытие <p> в <svg>. Код теоретически безвреден, поскольку опасный <img> элемент фактически находится в значении id атрибута.

Однако, когда мы пытаемся присвоить полученный HTML innerHTML'y, код будет видоизменяться в следующую форму:

image-15.png


Теперь <svg> элемент немедленно закрывается, и все, что следует, представляет собой обычный plain HTML. Это означает, что <style> элемент закрыт на </style>, а <img> тег, содержащий onerror атрибут, записывается в дерево DOM.

image-16.png


Вот и все! То есть mXSS в Chrome используется для обхода DOMPurify. Тот же трюк, вероятно, будет полезен и в обход других средств очистки HTML кода.

Вы можете поиграться с байпасом в jsbin, который я подготовил .


Резюме

В этой статье я описал недавно обнаруженный обход DOMPurify из-за поведения mXSS в Chrome. Проблема заключалась в том, что <svg></p> перезаписывался <svg><p></p></svg> браузером, а затем переписан <svg></svg><p></p> после назначения innerHTML. Этим можно злоупотреблять так, чтобы при первоначальном разборе HTML предполагалось, что некоторые элементы находятся внутри, <svg>, а в последующих - снаружи <svg>, что позволяет добавлять произвольные теги HTML.

Так что сам обход был:
HTML:
<svg></p><style><a id="</style><img src=1 onerror=alert(1)>">

После сообщения об обходе DOMPurify я заметил еще несколько проблем, о которых стоит упомянуть. Прежде всего, mXSS работает не только в Chrome, но и в Safari. Во-вторых, есть еще несколько вариантов:
  • Вместо <svg> вы также можете использовать <math>,
  • Вместо </p> вы также можете использовать </br>.
Если вы используете DOMPurify, вам следует немедленно обновить его до версии 2.0.1 или новее. Если по какой-либо причине вы не можете сделать это, попробуйте изменить конфигурацию по умолчанию, чтобы запретить <math> и <svg>:

Код:
DOMPurify.sanitize(input, {
     FORBID_TAGS: ['svg', 'math']
 });



Автор Michał Bentkowski
Перевод: tabac, специально для xss.pro
 
Боевой пример для выполнения XSS приведен на приложенном изображении.

index.jpg


mXSS принято использовать для обхода фильтраций, в том числе для обхода методов санитизации библиотеки DOMPurify.

Важное замечание, что для выполнения mXSS нужно несколько таких мутаций. Которые как раз таки происходят из-за применения Purify функций над фильтруемой стройкой. Первая мутация происходит во время применения функции sanitize объекта DOMPurify, а вторая во время присвоения свойства innerHTML. Наверное правильнее это вывести в подвид Reflected XSS.

by @empty_jack
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх