Client-Side Template Injection (CSTI)
В этом разделе мы рассмотрим уязвимость Client-Side Template Injection (CSTI) и то, как вы можете эксплуатировать их для XSS-атак. Эта техника атаки была впервые описана исследовательской командой PortSwigger — подробнее: XSS without HTML: Client-Side Template Injection with AngularJS. Хотя Client-Side Template Injection (CSTI) — проблема общего характера, мы сосредоточимся на примерах из фреймворка AngularJS, поскольку он наиболее распространён. Опишем, как создавать эксплойты, которые выходят из песочницы AngularJS (AngularJS sandbox), и как потенциально использовать возможности AngularJS для обхода Content Security Policy (CSP).
Что такое Client-Side Template Injection (CSTI)?
Client-Side Template Injection (CSTI) возникают, когда приложения, использующие клиентский шаблонный фреймворк, динамически встраивают пользовательский ввод в веб-страницы. При рендеринге страницы фреймворк сканирует её на наличие шаблонных выражений и выполняет все найденные. Атакующий может воспользоваться этим, передав вредоносное шаблонное выражение, которое запускает Cross-Site Scripting (XSS).
Что такое песочница AngularJS?
Песочница AngularJS — это механизм, который предотвращает доступ к потенциально опасным объектам, таким как window или document, в шаблонных выражениях AngularJS. Она также блокирует доступ к потенциально опасным свойствам, таким как __proto__. Несмотря на то, что команда AngularJS не рассматривала песочницу как полноценную меру безопасности, более широкое сообщество разработчиков часто считало иначе. Хотя изначально обойти песочницу было сложно, исследователи безопасности обнаружили множество способов сделать это. В результате песочница была удалена из AngularJS в версии 1.6. Тем не менее легаси‑приложения всё ещё используют более старые версии AngularJS и могут быть уязвимы.
Как работает песочница AngularJS?
Песочница работает путем анализа выражения, переписывания JavaScript-кода, а затем использования различных функций для проверки наличия в переписанном коде опасных объектов. Например, функция ensureSafeObject() проверяет, ссылается ли объект сам на себя. Это один из способов детектировать объект window. Конструктор Function определяется примерно так же — путём проверки, ссылается ли свойство constructor само на себя. Функция ensureSafeMemberName() проверяет каждый доступ к свойству объекта и, если в нём содержатся опасные свойства, такие как __proto__ или __lookupGetter__, объект будет заблокирован. Функция ensureSafeFunction() предотвращает вызовы call(), apply(), bind() или constructor().
Вы можете увидеть работу песочницы на практике, посетив этот fiddle и установив точку останова на строке 13275 файла angular.js. Переменная fnString содержит ваш код, поэтому вы можете посмотреть, как AngularJS его трансформирует.
Как работает выход из песочницы AngularJS?
Выход из песочницы (sandbox escape) заключается в том, чтобы заставить песочницу считать вредоносное выражение безобидным. Самый известный обход использует модифицированную функцию charAt() глобально внутри выражения:
'a'.constructor.prototype.charAt=[].joinКогда этот способ был изначально обнаружен, AngularJS не препятствовал такой модификации. Атака переопределяет функцию с помощью метода [].join, из‑за чего charAt() начинает возвращать все переданные ей символы, а не конкретный символ. Из‑за логики функции isIdent() в AngularJS она сравнивает то, что считает одиночным символом, с несколькими символами. Поскольку одиночные символы всегда «меньше» нескольких символов, функция isIdent() всегда возвращает true, как показано в примере:
isIdent = function(ch) {
return ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '_' === ch || ch === '$');
}
isIdent('x9=9a9l9e9r9t9(919)')После того как isIdent() введена в заблуждение, вы можете добавить вредоносный JavaScript. Например, выражение вроде $eval('x=alert(1)') будет допущено, потому что AngularJS трактует каждый символ как идентификатор. Обратите внимание, что нам нужно использовать функцию $eval() AngularJS, поскольку переопределение charAt() вступит в силу только после выполнения кода в песочнице. Эта техника обходит песочницу и позволяет выполнять произвольный JavaScript. XSS without HTML: Client-Side Template Injection with AngularJS
Построение продвинутого выхода из песочницы AngularJS
Предположим, вы освоили базовый обход песочницы, но сталкиваетесь с сайтами, которые жёстче ограничивают допустимые символы. Например, сайт может запрещать использование двойных или одиночных кавычек. В этой ситуации вам нужно использовать такие функции, как String.fromCharCode(), чтобы генерировать символы. Хотя AngularJS запрещает доступ к конструктору String внутри выражения, это можно обойти, используя свойство constructor у строкового значения. Очевидно, для этого нужна строка, так что, чтобы построить такую атаку, вам придётся найти способ создать строку без использования одинарных или двойных кавычек.
В стандартном обходе песочницы вы бы использовали $eval() для выполнения JavaScript, но в лабораторной ниже функция $eval() не определена. К счастью, можно использовать фильтр orderBy. Типичный синтаксис фильтра orderBy:
Обратите внимание, что оператор | имеет иное значение, чем в JavaScript. Обычно это побитовое ИЛИ, но в AngularJS он обозначает применение фильтра. В коде выше мы передаём массив [123] в фильтр orderBy. Двоеточие указывает аргумент, передаваемый фильтру — в данном случае строку. Обычно orderBy сортирует объект, но он также принимает выражение, что позволяет использовать его для передачи полезной нагрузки.
Как работает обход CSP в AngularJS?
Обходы Content Security Policy (CSP) работают схожим образом с обычными обходами песочницы, но обычно включают некоторую HTML‑инъекцию. Когда в AngularJS активирован CSP‑режим, он по‑другому парсит шаблонные выражения и избегает использования конструктора Function. Это означает, что стандартный обход песочницы, описанный выше, больше не сработает.
В зависимости от конкретной политики CSP будут блокироваться JavaScript‑события. Однако AngularJS определяет собственные события, которые можно использовать вместо них. Внутри события AngularJS определяет специальный объект $event, который просто ссылается на объект браузерного события. Вы можете использовать этот объект, чтобы выполнить обход CSP. В Chrome у объекта $event/event есть специальное свойство path. Это свойство содержит массив объектов которые привели к выполнению события. Последний элемент в этом массиве — всегда объект window, который мы можем использовать для выхода из песочницы. Передав этот массив в фильтр orderBy, мы можем проитерироваться по массиву и использовать последний элемент (объект window), чтобы выполнить глобальную функцию, такую как alert().
Пример:
Обратите внимание на использование функции from(), которая позволяет конвертировать объект в массив и вызвать заданную функцию (указанную вторым аргументом) для каждого элемента массива. В данном случае мы вызываем функцию alert(). Мы не можем вызвать функцию напрямую, потому что песочница AngularJS распарсит код и обнаружит, что используется объект window для вызова функции. Использование from() фактически «прячет» windowот песочницы, позволяя инжектировать вредоносный код. AngularJS CSP bypass in 56 characters
Обход CSP с выходом из песочницы AngularJS
В следующей лабораторной есть ограничение на длину, поэтому вектор выше не подойдёт. Чтобы эксплуатировать уязвимость в данной лабораторной, вам нужно подумать о различных способах «спрятать» объект window от песочницы AngularJS. Один из способов — использовать функцию array.map() следующим образом:
map() принимает функцию в качестве аргумента и вызывает её для каждого элемента массива. Это обходит песочницу, поскольку ссылка на функцию alert() используется без явного упоминания window. Чтобы решить лабораторную, попробуйте различные способы выполнить alert() без срабатывания обнаружения window в AngularJS.
Как предотвратить уязвимости Client Side Template Injection (CSTI)
Чтобы предотвратить уязвимости Client Side Template Injection (CSTI), избегайте использования недоверенного пользовательского ввода для генерации шаблонов или выражений. Если это невозможно, рассмотрите вариант фильтрации синтаксиса шаблонных выражений из пользовательского ввода перед встраиванием его в клиентские шаблоны.
Обратите внимание, что HTML‑кодирования недостаточно для предотвращения атак Client Side Template Injection (CSTI), потому что фреймворки выполняют HTML‑декодирование соответствующего контента перед поиском и исполнением шаблонных выражений.
Дополнительные материалы
Last updated