(SSTI) Инъекция шаблона на стороне сервера
Эта техника впервые была задокументирована PortSwigger Research в докладе на конференции Server-Side Template Injection: RCE (удаленное выполнение кода) for the Modern Web App.
В этом разделе мы обсудим, что такое инъекция серверных шаблонов, и изложим базовую методологию эксплуатации уязвимостей инъекции серверных шаблонов. Мы также предложим способы гарантировать, что ваше собственное использование шаблонов не подвергнет вас инъекции серверных шаблонов.
Лабораторные работы
Если вы уже знакомы с основными концепциями уязвимостей инъекции серверных шаблонов и просто хотите попрактиковаться в их эксплуатации на реалистичных, намеренно уязвимых целях, вы можете получить доступ ко всем лабораторным работам по этой теме по ссылке ниже.
Эта техника впервые была задокументирована PortSwigger в нашем исследовательском докладе 2015 года на эту тему. Если вам интересно, как нам удалось эксплуатировать некоторые из этих уязвимостей на живых сайтах, полный отчёт доступен на нашей странице исследований.
Что такое инъекция серверных шаблонов?
Инъекция серверных шаблонов — это когда злоумышленник может использовать нативный синтаксис шаблона, чтобы внедрить вредоносный пейлоад в шаблон, который затем выполняется на стороне сервера.
Движки шаблонов предназначены для генерации веб-страниц путём объединения фиксированных шаблонов с изменяемыми данными. Атаки инъекции серверных шаблонов могут возникать, когда пользовательский ввод конкатенируется напрямую в шаблон, а не передается как данные. Это позволяет злоумышленникам внедрять произвольные директивы шаблонов, чтобы манипулировать движком шаблонов, зачастую позволяя им полностью захватывать контроль над сервером. Как следует из названия, пейлоады инъекции серверных шаблонов доставляются и выполняются на стороне сервера, что потенциально делает их гораздо опаснее типичной инъекции клиентских шаблонов.
Каков эффект инъекции серверных шаблонов?
Уязвимости инъекции серверных шаблонов могут подвергать сайты различным атакам в зависимости от конкретного движка шаблонов и того, как именно приложение его использует. В некоторых редких случаях эти уязвимости не представляют реального риска для безопасности. Однако в большинстве случаев воздействие инъекции серверных шаблонов может быть катастрофическим.
На наиболее серьёзном конце шкалы злоумышленник потенциально может добиться удалённого выполнения кода (remote code execution), полностью захватив контроль над сервером бэкенда и используя его для проведения других атак на внутреннюю инфраструктуру.
Даже в случаях, когда полное удалённое выполнение кода невозможно, злоумышленник часто всё равно может использовать инъекцию серверных шаблонов как основу для множества других атак, потенциально получая доступ на чтение к конфиденциальным данным и произвольным файлам на сервере.
Как возникают уязвимости инъекции серверных шаблонов?
Уязвимости инъекции серверных шаблонов возникают, когда пользовательский ввод конкатенируется в шаблоны, а не передается как данные.
Статические шаблоны, которые просто предоставляют плейсхолдеры, в которые подставляется динамический контент, как правило, не уязвимы для инъекции серверных шаблонов. Классический пример — электронное письмо, приветствующее каждого пользователя по имени, например следующий фрагмент шаблона Twig:
Это не уязвимо для инъекции серверных шаблонов, потому что имя пользователя просто передаётся в шаблон как данные.
Однако поскольку шаблоны — это просто строки, веб-разработчики иногда напрямую конкатенируют пользовательский ввод в шаблоны перед рендерингом. Возьмём похожий пример, что и выше, но на этот раз пользователи могут настраивать части письма перед отправкой. Например, они могут выбрать используемое имя:
В этом примере вместо того, чтобы статическое значение передавалось в шаблон, часть самого шаблона динамически формируется с использованием параметра GET name. Поскольку синтаксис шаблона оценивается на стороне сервера, это потенциально позволяет злоумышленнику поместить пейлоад инъекции серверных шаблонов в параметр name следующим образом:
Подобные уязвимости иногда возникают случайно из-за неудачного дизайна шаблонов людьми, незнакомыми с последствиями для безопасности. Как в примере выше, вы можете увидеть разные компоненты, некоторые из которых содержат пользовательский ввод, конкатенированные и встроенные в шаблон. В некотором смысле это похоже на уязвимости SQL injection, возникающие в плохо написанных подготовленных выражениях.
Однако иногда такое поведение реализуется намеренно. Например, некоторые сайты преднамеренно позволяют определённым привилегированным пользователям, таким как контент-редакторы, редактировать или отправлять пользовательские шаблоны по замыслу. Это явно представляет огромный риск для безопасности, если злоумышленник сможет скомпрометировать учётную запись с такими привилегиями.
Построение атаки инъекции серверных шаблонов
Выявление уязвимостей инъекции серверных шаблонов и создание успешной атаки обычно включает следующий процесс высокого уровня.

Обнаружение (Detect)
Уязвимости инъекции серверных шаблонов часто остаются незамеченными не потому, что они сложны, а потому, что они очевидны лишь аудиторам, которые специально их ищут. Если вы сможете обнаружить наличие уязвимости, её эксплуатация может оказаться удивительно простой. Это особенно верно в несандбоксированных средах.
Как и с любой уязвимостью, первый шаг к эксплуатации — уметь её найти. Возможно, самый простой начальный подход — попытаться зафуззить шаблон, внедрив последовательность специальных символов, обычно используемых в выражениях шаблонов, таких как
Если возникает исключение, это указывает на то, что внедрённый синтаксис шаблона потенциально интерпретируется сервером каким-то образом. Это один из признаков того, что может существовать уязвимость к инъекции серверных шаблонов.
Уязвимости инъекции серверных шаблонов возникают в двух различных контекстах, каждый из которых требует собственного метода обнаружения. Независимо от результатов ваших попыток фаззинга, важно также попробовать следующие подходы, специфичные для контекста. Если фаззинг оказался неубедительным, уязвимость всё ещё может проявить себя с использованием одного из этих подходов. Даже если фаззинг и указывал на уязвимость инъекции шаблонов, вам всё равно нужно определить её контекст, чтобы эксплуатировать её.
Контекст обычного текста (Plaintext context)
Большинство языков шаблонов позволяют свободно вводить контент либо напрямую с помощью HTML-тегов, либо с помощью нативного синтаксиса шаблона, который будет отрендерен в HTML на бэкенде до отправки HTTP-ответа. Например, в Freemarker строка render('Hello ' + username) будет отрендерена в нечто вроде Hello Carlos.
Иногда это может быть использовано для XSS (межсайтовый скриптинг) и фактически часто принимается за простую уязвимость XSS. Однако, задав математические операции в качестве значения параметра, мы можем проверить, является ли это также потенциальной точкой входа для атаки инъекции серверных шаблонов.
Например, рассмотрим шаблон, содержащий следующий уязвимый код:
Во время аудита мы можем протестировать инъекцию серверных шаблонов, запросив URL вида:
Если результирующий вывод содержит Hello 49, это показывает, что математическая операция вычисляется на стороне сервера. Это хорошее доказательство концепции уязвимости инъекции серверных шаблонов.
Обратите внимание, что конкретный синтаксис, необходимый для успешного вычисления математической операции, будет варьироваться в зависимости от используемого движка шаблонов. Мы обсудим это подробнее на шаге Идентификация (Identify).
Контекст кода (Code context)
В других случаях уязвимость проявляется тем, что пользовательский ввод помещается внутрь выражения шаблона, как мы видели ранее в нашем примере с письмом. Это может принимать форму имени переменной, контролируемой пользователем, помещённой внутрь параметра, например:
На сайте результирующий URL будет выглядеть примерно так:
Это будет отрендерено в выводе как Hello Carlos, например.
Этот контекст легко упустить во время оценки, потому что он не приводит к очевидному XSS и практически неотличим от простого поиска по hashmap. Один из методов тестирования инъекции серверных шаблонов в этом контексте — сначала убедиться, что параметр не содержит прямой уязвимости XSS, внедрив произвольный HTML в значение:
При отсутствии XSS это обычно приведёт к пустой записи в выводе (просто Hello без имени пользователя), экранированным тегам или сообщению об ошибке. Следующий шаг — попытаться выйти из выражения, используя распространённый синтаксис шаблонов, и попытаться внедрить произвольный HTML после него:
Если это снова приводит к ошибке или пустому выводу, вы либо использовали синтаксис неправильного языка шаблонов, либо, если никакой «шаблонный» синтаксис не оказывается валидным, инъекция серверных шаблонов невозможна. В качестве альтернативы, если вывод рендерится корректно вместе с произвольным HTML, это ключевой признак наличия уязвимости инъекции серверных шаблонов:
Идентификация (Identify)
После обнаружения потенциала инъекции шаблонов следующий шаг — определить движок шаблонов.
Несмотря на огромное количество языков шаблонов, многие из них используют очень схожий синтаксис, специально подобранный так, чтобы не конфликтовать с HTML-символами. В результате относительно просто создать зондирующие пейлоады, чтобы проверить, какой движок шаблонов используется.
Часто достаточно отправить некорректный синтаксис, потому что в результате сообщение об ошибке скажет вам, какой именно движок шаблонов используется, а иногда даже какую версию. Например, некорректное выражение <%=foobar%> вызывает следующий ответ от движка ERB на Ruby:
В противном случае вам нужно будет вручную тестировать различные специфичные для языка пейлоады и изучать, как движок шаблонов их интерпретирует. Используя процесс исключения на основе того, какой синтаксис кажется валидным или невалидным, вы сможете сузить круг быстрее, чем можно ожидать. Распространённый способ сделать это — внедрить произвольные математические операции, используя синтаксис из разных движков шаблонов. Затем вы можете наблюдать, были ли они успешно вычислены. Чтобы помочь в этом процессе, вы можете использовать дерево решений, подобное следующему:

Следует учитывать, что один и тот же пейлоад иногда может вернуть успешный ответ более чем в одном языке шаблонов. Например, пейлоад {{7*'7'}} возвращает 49 в Twig и 7777777 в Jinja2. Поэтому важно не делать выводов на основе единственного успешного ответа.
Эксплуатация (Exploit)
После обнаружения потенциальной уязвимости и успешного определения движка шаблонов вы можете начать пытаться найти способы её эксплуатации.
Как предотвратить уязвимости инъекции серверных шаблонов
Лучший способ предотвратить инъекцию серверных шаблонов — не позволять пользователям модифицировать или отправлять новые шаблоны. Однако иногда это неизбежно из-за бизнес-требований.
Один из самых простых способов избежать введения уязвимостей инъекции серверных шаблонов — всегда использовать «лишённый логики» движок шаблонов, такой как Mustache, если только это не абсолютно необходимо. Максимальное разделение логики и представления может существенно снизить вашу экспозицию к наиболее опасным атакам на основе шаблонов.
Ещё одна мера — выполнять пользовательский код только в изолированной среде (sandbox), где потенциально опасные модули и функции полностью удалены. К сожалению, изоляция недоверенного кода по своей природе сложна и подвержена обходам.
Наконец, другой дополнительный подход — принять тот факт, что произвольное выполнение кода практически неизбежно, и применить собственную изоляцию, развернув вашу шаблонную среду в жёстко ограниченном Docker-контейнере, например.
Last updated