Template Injection in Frontend - CSTI
CSTI, то есть Client Side Template Injection, относится к внедрению шаблона во фронтенде. Поскольку есть версия для фронтенда, существует и соответствующая версия для бэкенда, называемая SSTI, что означает "Внедрение шаблона на стороне сервера".
Прежде чем рассматривать версию для фронтенда, давайте посмотрим на версию для бэкенда.
Server Side Template Injection
При написании бэкенд-кода, который должен выводить HTML, вы можете выбрать его прямой вывод, как в чистом PHP:
Однако, когда в HTML есть динамические части, код становится все более сложным. Поэтому в реальной разработке обычно используют что-то, что называется движком шаблонов, о котором мы кратко упоминали, говоря о санитизации.
Например, на странице некоего блога часть шаблона выглядит так:
При рендеринге мне нужно просто передать объект post и объединить его с шаблоном, чтобы создать полноценную страницу статьи.
Внедрение шаблона не означает, что "злоумышленники могут манипулировать такими данными, как post," а скорее "злоумышленники могут манипулировать самим шаблоном".
Например, предположим, что у нас есть служба маркетинговых электронных писем. Обычно компании загружают в нее данные пользователей и устанавливают собственные шаблоны, такие как:
Когда шаблон напрямую используется бэкендом, используя Python с Jinja2 в качестве примера, он выглядит так:
Финальный вывод:
Выглядит нормально, но что, если мы модифицируем шаблон? Например так:
Вывод станет: Output: Darwin, а Darwin - это результат выполнения команды uname.
Простыми словами, вы можете думать о содержимом в {{}} как о коде, который движок шаблонов выполнит за вас.
Хотя мы обычно писали просто {{name}}, на самом деле можно выполнить больше операций, например, {{ name + email }}. В вышеуказанном примере он начинается с self и использует Python, чтобы прочитать __import__, позволяя импортировать другие модули и достигать выполнения команды.
Уязвимости, которые позволяют злоумышленникам контролировать шаблон, называются внедрением шаблона. Когда это происходит на бэкенде, это называется SSTI, а когда это происходит на фронтенде, это называется CSTI.
Метод защиты прост: не рассматривайте ввод пользователя как часть шаблона. Если вам это все же необходимо, убедитесь, что движок шаблонов предоставляет функцию песочницы, которая позволяет выполнять ненадежный код в безопасной среде.
Примеры SSTI в реальности
Первый пример - это уязвимость, обнаруженная Orange в Uber в 2016 году. Однажды Orange заметил 2 в письме, отправленном Uber, и вспомнил, что он ввел {{ 1+1 }} в поле имени. Это обычная техника при поиске уязвимостей SSTI, когда в поля для ввода вводятся множество полезных нагрузок, чтобы проверить, есть ли какие-либо проблемы SSTI на основе результатов.
Затем они использовали упомянутую выше технику, чтобы найти, какие переменные можно использовать и объединить. Так как Uber также использует Jinja2, окончательный полезная нагрузка очень похожа на то, что мы только что написали, и они успешно достигли RCE с помощью SSTI.
Второй пример - это внедрение шаблонов Handlebars SSTI в Shopify, о котором сообщил Mahmoud Gamal в 2019 году.
Бэкенд продавцов Shopify имеет функцию, которая позволяет продавцам настраивать письма, отправляемые пользователям (аналогично приведенному ранее примеру). Они могут использовать синтаксис типа {{ order.number }} для настройки содержимого. Бэкенд использует Node.js с движком шаблонов Handlebars.
Поскольку Handlebars имеет некоторую защиту и более сложен, хакеры потратили много времени, чтобы разобраться, как атаковать его.
В конце концов, наличие SSTI - это одно, а вот не каждый шаблонный движок можно эксплуатировать для RCE.
Окончательная полезная нагрузка, которую они придумали, была очень длинной:
Client Side Template Injection
Понимание CSTI становится проще после понимания SSTI, поскольку принципы схожи, единственное отличие в том, что этот шаблон - это шаблон на стороне клиента.
Подождите, есть ли шаблоны на фронтенде? Безусловно!
Ключевое здесь - параметр под названием template. Если вы измените {{ count }} на {{ constructor.constructor('alert(1)')() }}, вы увидите всплывающее окно с предупреждением.
Необходимо использовать constructor.constructor('alert(1)')() потому что шаблон не может напрямую получить доступ к window, поэтому создается новая функция через конструктор функции.
Шаблоны следует рассматривать как исполняемый код, и пользователю никогда не следует разрешать управлять шаблонами.
Кстати, знаете ли вы разницу между AngularJS и Angular?
Когда он был впервые выпущен в 2010 году, он назывался AngularJS, и номера версий были 0.x.x или 1.x.x. Но после версии 2 его переименовали в Angular, схожее использование, но полностью переписанный дизайн. Мы в основном будем ссылаться на старую версию, AngularJS, потому что она имеет больше проблем из-за своего возраста, и это библиотека, подходящая для помощи в атаках.
Когда AngularJS был впервые выпущен, выполнение произвольного кода также было возможно с использованием {{ constructor.constructor('alert(1)')() }}. Однако, начиная с версии 1.2.0, был добавлен механизм песочницы, чтобы предотвратить доступ к window любым возможным способом. Но когда дело доходит до атаки и защиты, исследователи безопасности не отстают, и они нашли способы обхода песочницы.
В версиях AngularJS 1.x этот процесс был более удобным и простым, требуя только элемент с ng-app:
Идеально было бы, если бы весь фронтенд контролировался AngularJS, с общением на бэкенд через API, а бэкенд не должен был бы заниматься рендерингом представления, однако в то время концепция SPA еще не была популярна, и многие веб-сайты все еще имели бэкенд, отвечающий за рендеринг представления. Поэтому было весьма вероятно написать следующий код:
Хотя в коде выше закодирован ввод, в {{ alert(1) }} нет опасных символов, поэтому это все равно может привести к XSS.
Метод защиты такой же, как у SSTI. Никогда не рассматривайте ввод пользователя как часть содержимого шаблона, или это легко может вызвать проблемы.
Практический пример CSTI
Давайте приведём интересный случай в качестве примера. Масато Кинугава, исследователь в области кибербезопасности из Японии, продемонстрировал уязвимость удаленного выполнения кода (RCE) в программном обеспечении для связи Microsoft Teams во время Pwn2Own 2022. Отправив сообщение целевой стороне, можно выполнить код на их компьютере! Эта уязвимость принесла приз в размере $150,000 на Pwn2Own.
Программное обеспечение Teams для настольных компьютеров создано с использованием Electron, по сути, это веб-страница. Чтобы добиться RCE, обычно первым шагом является поиск XSS, который позволяет выполнить код JavaScript на веб-странице.
Teams также обрабатывает ввод пользователя. И на клиентской, и на серверной стороне есть санитайзеры, которые удаляют странные элементы и гарантируют, что окончательное отрендеренное содержимое безопасно. Хотя некоторый HTML можно контролировать, многие атрибуты и содержимое фильтруются.
Например, даже для имен классов разрешены только определенные имена классов. Масато обнаружил, что у санитайзера есть некоторый простор для манипуляций с именами классов. Например, есть правило типа swift-*, поэтому и swift-abc, и swift-; [] ()'% допускается в качестве имен классов.
Но что пользы только от манипуляций с именами классов?
Вот ключ: веб-страница Teams написана на AngularJS, у которого есть много магических функций. Одна из них – атрибут ng-init, используемый для инициализации, вот так:
Это отобразит test на странице, указывая на то, что код внутри ng-init выполнен.
Так что если вы измените его на ng-init = "constructor.constructor ('alert (1)') ()", появится предупреждающее окно.
Какое это имеет отношение к ранее упомянутому названию класса? Оказывается, что этот ng-init также можно использовать внутри имени класса:
Поэтому, объединив ранее упомянутые правила проверки имен классов, можно составить имя класса, содержащее ранее упомянутую полезную нагрузку, и успешно выполнить XSS.
AngularJS и CSP Bypass
На практике AngularJS наиболее часто используется для обхода CSP. Если вы можете найти AngularJS в разрешенных путях CSP, существует большая вероятность его обхода. Например:
Хотя это может показаться простым, при более тщательном рассмотрении это не так просто. Подумайте, CSP не имеет unsafe-eval, поэтому ни одна строка не может быть выполнена как код. Но тогда как все эти строки внутри ng-focus выполняются? Разве это не выполнение строк как кода?
Таким образом, можно считать, что AngularJS реализует свой собственный eval для выполнения строк как кода, не используя эти стандартные функции.
Заключение
Рассмотренная на этот раз CSTI также является типом атаки "непрямого выполнения JavaScript".
Когда вы кодируете все выводы и думаете, что это безопасно, забывая, что на вашем фронтенде есть AngularJS, злоумышленники могут достичь XSS через казалось бы безопасные {{}}, используя CSTI.
Хотя сейчас сайтов, использующих AngularJS, становится все меньше и меньше, и все меньше людей рассматривают ввод пользователя как часть шаблона. Многие уязвимости попросту еще не были обнаружены.
Если ваш сервис использует AngularJS, убедитесь, что нет проблем с CSTI.
Last updated