Вчера была выпущена новая версия DOMPurify (очень популярной библиотеки очистки XSS), которая исправила обход, о котором мы сообщали. В этом посте я покажу, как именно выглядел обход, которому предшествует общая информация о DOMPurify и как он работает. Если вы знаете, как работают очистители и что такое mXSS - вы можете сразу перейти к пункту mXSS в Chromium (и Safari) .
HTML sanitizers – зачем они нужны и как они работают
Довольно распространенный вариант использования в веб-приложениях заключается в том, что пользователям разрешается вводить некоторые HTML-данные, в основном в форме полнофункциональных редакторов, что позволяет включать форматирование в текст (например, жирный , курсив и т.д.). Эта функция обычно возможна в веб-почте, блог-платформах и т.д. Основная проблема заключается в том, что пользователь может включить вредоносный код HTML/JavaScript и внедрить XSS.
Именно здесь в игру вступают HTML-sanitizers или по-русски "Очистители XSS". Их главная цель - взять ненадежный ввод, очистить его и создать безопасный HTML (HTML с удаленными опасными тегами).
Идея HTML-sanitizers
HTML-sanitizers обычно выполняют очистку путем анализа входных данных (есть несколько способов сделать это из JavaScript, один из примеров - использование DOMParser.prototype.parseFromString метода). Затем HTML-sanitizers получают список разрешенных элементов и атрибутов, обходят дерево DOM и удаляют все, чего нет в списке (это немного упрощено, потому что реальные sanitizers зачастую более сложны, чем это, но для примера это довольно).
Итак, давайте предположим, что у нас есть HTML-sanitizer со следующим списком разрешений:
После парсинга мы получим следующее дерево DOM:
Небезопасный HTML
Итак, есть две вещи, которые следует удалить:
Безопасный, очищенный HTML
Теперь у нас есть «безопасное» дерево DOM со всеми недопустимыми элементами или атрибутами. Следовательно, очиститель выдаст следующую строку после выполнения очистки:
Теперь это безопасный HTML-фрагмент, который можно без опасений вставлять в наше DOM-дерево, верно? Это верно, но с одной важной оговоркой: так называемая мутация XSS.
Что такое мутация XSS?
Основным источником изучения мутации XSS (mXSS) по-прежнему является статья Марио Хайдериха, написанная в 2013 году, которая называется mXSS Attacks: Атака хорошо защищенных веб-приложений с использованием мутаций innerHTML. В следующих параграфах я дам вам краткий обзор того, что такое
Правая часть назначения автоматически анализируется и вставляется в дерево DOM как чайлд-элементы element. Дело в том, что
Написание и чтение из innerHTML
Как видите, сразу после записи
Но мы открываем ящик Пандоры, когда понимаем, что иногда вход может изменяться несколько раз. Предположим, у нас есть следующее выражение:
На первый взгляд, назначение
mXSS в Chromium
Так что обход
Попытка поместить
Я присваиваю тег
Но давайте посмотрим, что произойдет, когда я попытаюсь поместить закрывающий
Попытка поместить
В, возможно, удивительном повороте событий,
mXSS в Chrome
Таким образом, полезная нагрузка
Злоупотребление mXSS для обхода DOMPurify
Попробуем присвоить следующую строку
В этом фрагменте DOM нет ничего плохого по своей сути. Все теги (
… внезапно появился alert!
Здесь происходит злоупотребление определенным поведением
То же самое не относится к SVG. Давайте попробуем точно такой же пример, но с
Как видите, теперь у
Итак, теперь давайте посмотрим пример с DOMPurify:
В этом случае браузер предполагает, что оба
Однако, когда мы пытаемся присвоить полученный HTML innerHTML'y, код будет видоизменяться в следующую форму:
Теперь
Вот и все! То есть mXSS в Chrome используется для обхода DOMPurify. Тот же трюк, вероятно, будет полезен и в обход других средств очистки HTML кода.
Вы можете поиграться с байпасом в jsbin, который я подготовил .
Резюме
В этой статье я описал недавно обнаруженный обход DOMPurify из-за поведения mXSS в Chrome. Проблема заключалась в том, что <svg></p> перезаписывался <svg><p></p></svg> браузером, а затем переписан <svg></svg><p></p> после назначения innerHTML. Этим можно злоупотреблять так, чтобы при первоначальном разборе HTML предполагалось, что некоторые элементы находятся внутри, <svg>, а в последующих - снаружи <svg>, что позволяет добавлять произвольные теги HTML.
Так что сам обход был:
После сообщения об обходе DOMPurify я заметил еще несколько проблем, о которых стоит упомянуть. Прежде всего, mXSS работает не только в Chrome, но и в Safari. Во-вторых, есть еще несколько вариантов:
Автор Michał Bentkowski
Перевод: tabac, специально для xss.pro
HTML sanitizers – зачем они нужны и как они работают
Довольно распространенный вариант использования в веб-приложениях заключается в том, что пользователям разрешается вводить некоторые HTML-данные, в основном в форме полнофункциональных редакторов, что позволяет включать форматирование в текст (например, жирный , курсив и т.д.). Эта функция обычно возможна в веб-почте, блог-платформах и т.д. Основная проблема заключается в том, что пользователь может включить вредоносный код HTML/JavaScript и внедрить XSS.
Именно здесь в игру вступают HTML-sanitizers или по-русски "Очистители XSS". Их главная цель - взять ненадежный ввод, очистить его и создать безопасный HTML (HTML с удаленными опасными тегами).
Идея HTML-sanitizers
HTML-sanitizers обычно выполняют очистку путем анализа входных данных (есть несколько способов сделать это из JavaScript, один из примеров - использование DOMParser.prototype.parseFromString метода). Затем HTML-sanitizers получают список разрешенных элементов и атрибутов, обходят дерево DOM и удаляют все, чего нет в списке (это немного упрощено, потому что реальные sanitizers зачастую более сложны, чем это, но для примера это довольно).
Итак, давайте предположим, что у нас есть HTML-sanitizer со следующим списком разрешений:
- Элементы:
<div>,<b>,<i>и<img>. - Атрибут: только
src.
HTML:
<div>I am trying to be <i>malicious</i> <u>here</u>! <img src=1 onerror=alert(1)></div>
После парсинга мы получим следующее дерево DOM:
Небезопасный HTML
Итак, есть две вещи, которые следует удалить:
<u>элемент, не находящийся в allow-списке,onerrorатрибут, не находящийся в allow-списке.
Безопасный, очищенный 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'
innerHTML означает, что браузер может изменять строку, которую мы хотели вставить. Например, если я попытаюсь прочитать element.innerHTML выше, я получу следующий результат:
Написание и чтение из innerHTML
Как видите, сразу после записи
innerHTML, значение, которое я получаю, отличается. Это ожидаемо. В конце концов, пользователь может ввести неработающий HTML, и браузер должен это исправить.Но мы открываем ящик Пандоры, когда понимаем, что иногда вход может изменяться несколько раз. Предположим, у нас есть следующее выражение:
HTML:
element.innerHTML = element.innerHTML
innerHTML не должно иметь значения. Но дело в том, что из-за ошибок в браузерах иногда это происходит. И это как раз тогда, когда происходит MXSS.mXSS в Chromium
Так что обход
DOMPurify может произойти, потому что я нашел новый вектор mXSS в текущей версии Chrome (77). Давайте начнем с примера:
Попытка поместить
<p> в <svg>Я присваиваю тег
<svg>тегу <p> (являющийся дочерним элементом <svg>). Однако, как вы можете видеть в дереве DOM, <p> элемент фактически «выпрыгнул» из <svg>. Это произошло потому, что это недопустимый тег внутри <svg>, поэтому браузер решил закрыть его и открыть <p> после него.Но давайте посмотрим, что произойдет, когда я попытаюсь поместить закрывающий
</p> тег в SVG:
Попытка поместить
</p> в <svg>В, возможно, удивительном повороте событий,
<p> элемент теперь является потомком <svg>. Кроме того, как вы можете видеть внизу, Chrome автоматически добавил открывающий <p> тег. Что означает, что если я попытаюсь присвоить innerHTML себе, он будет мутировать!
mXSS в Chrome
Таким образом, полезная нагрузка
<svg></p> является базой для mXSS, потому что она мутирует при назначении innerHTML; контент, который изначально находится внутри <svg>, выпрыгивает из него. Остается вопрос, как его использовать.Злоупотребление mXSS для обхода DOMPurify
Попробуем присвоить следующую строку
innerHTML элементу DOM:
HTML:
<svg></p><style><a id="</style><img src=1 onerror=alert(1)>">
В этом фрагменте DOM нет ничего плохого по своей сути. Все теги (
<div>, <svg>, <p>, <style> и <a>) и атрибут id разрешены DOMPurify в дефолтной конфигурации. Так что, это ничего не меняет в этом коде. Однако, когда мы пытаемся присвоить innerHTML ему же ...
… внезапно появился alert!
Здесь происходит злоупотребление определенным поведением
<svg> элемента. По сути, когда вы открываете <svg> в своем HTML-коде, правила синтаксического анализа браузера меняются и становятся ближе к синтаксическому анализу XML, чем к синтаксическому анализу HTML. Одно из основных отличий заключается в том, что определенные теги в HTML не могут иметь "детей" при десериализации из текста. Примером является <style>. Если вы посмотрите на спецификацию HTML, вы обнаружите, что ее модель контента - это Text. Даже если вы попытаетесь поместить элемент в a <style>, он будет рассматриваться как текст:
То же самое не относится к SVG. Давайте попробуем точно такой же пример, но с
<style> ребенком <svg>:
Как видите, теперь у
<style> есть дочерний элемент.Итак, теперь давайте посмотрим пример с DOMPurify:
В этом случае браузер предполагает, что оба
</p> и <style> являются дочерними элементами тега <svg>, в результате чего <a> элемент является дочерним элементом <style>. Тем не менее, код немного видоизменяется и теперь есть также открытие <p> в <svg>. Код теоретически безвреден, поскольку опасный <img> элемент фактически находится в значении id атрибута.Однако, когда мы пытаемся присвоить полученный HTML innerHTML'y, код будет видоизменяться в следующую форму:
Теперь
<svg> элемент немедленно закрывается, и все, что следует, представляет собой обычный plain HTML. Это означает, что <style> элемент закрыт на </style>, а <img> тег, содержащий onerror атрибут, записывается в дерево DOM.
Вот и все! То есть 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>.
<math> и <svg>:
Код:
DOMPurify.sanitize(input, {
FORBID_TAGS: ['svg', 'math']
});
Автор Michał Bentkowski
Перевод: tabac, специально для xss.pro