SSTI: Twig createTemplate() Injection
Пользовательский ввод передаётся в Twig createTemplate(), что позволяет выполнить произвольный код на сервере.
mediumphpPro
Задача
# Server-Side Template Injection в Twig: от подстановки до RCE
## Сценарий
Перед вами PHP-приложение интернет-магазина SecureShop. На странице профиля авторизованный пользователь может настроить персональное приветствие — поле «Bio Template». В интерфейсе явно указано, что поддерживается шаблонный синтаксис: пользователь может включить в bio подстановки своего имени, чтобы сообщение получалось живее. Сервер использует популярный для PHP шаблонизатор Twig (версия 3.x) и рендерит bio при каждом открытии профиля.
С точки зрения разработчика всё выглядит логично — это же просто пользовательская кастомизация в фиксированном поле, набор переменных строго ограничен. Но обработчик профиля передаёт строку bio в шаблонизатор не как **данные**, а как **исходный код шаблона**: пользовательский ввод компилируется и исполняется как полноценная Twig-программа. А Twig — мощный язык: он умеет вычислять выражения, применять цепочки фильтров, работать с коллекциями и (что критично) запускать встроенные PHP-функции через специальные фильтры высшего порядка.
Это значит, что любая Twig-конструкция, которую пользователь введёт в bio, будет интерпретирована шаблонизатором на сервере. Простая «персонализация приветствия» оказывается полноценным каналом для Server-Side Template Injection. А поскольку Twig в PHP-окружении имеет фильтры, принимающие callable-имена (имя PHP-функции в виде строки), от детектора SSTI до полноценного Remote Code Execution — буквально один шаг: применить фильтр к коллекции из одного элемента, передав имя нужной системной функции.
## Что мы тестируем
Платформа определяет три уровня успешного исследования этой лабы:
1. **Обнаружение точки внедрения.** Найти в личном кабинете поле произвольного текста, которое рендерится как Twig-шаблон с подстановками. Для входа использовать стандартную тестовую учётную запись.
2. **Подтверждение SSTI.** Доказать, что сервер действительно интерпретирует значение поля как код шаблона, а не возвращает его «как есть». Стандартный детектор — арифметическое выражение, которое нормальный текст вернуть «вычисленным» не может.
3. **Эскалация SSTI → RCE → утечка флага.** От арифметического подтверждения перейти к выполнению произвольной PHP-функции через цепочку Twig-фильтров и прочитать переменную окружения процесса, содержащую CTF-флаг.
## Класс уязвимости
Server-Side Template Injection возникает там, где пользовательский ввод используется как **код шаблона**, а не как **данные шаблона**. Разница принципиальна: данные передаются в заранее написанный шаблон через контекст и при выводе экранируются; код шаблона компилируется, и любые управляющие конструкции (выражения, фильтры, вызовы функций) исполняются.
Для Twig типовая ошибка разработчика — вызов `createTemplate($userInput)`. Этот метод предназначен для динамических шаблонов в специальных сценариях (плагины, конструкторы писем), а не для пользовательского ввода. Любое выражение `{{ ... }}` в строке `$userInput` исполнится при `render()`.
## Цели
| # | Цель |
|---|------|
| 1 | Войти в приложение под тестовой учётной записью и найти поле, которое позволяет настроить персональное приветствие |
| 2 | Подтвердить SSTI через арифметическое выражение (детектор) |
| 3 | Через цепочку Twig-фильтров вызвать встроенную PHP-функцию и получить значение переменной окружения с флагом |
| 4 | Сдать полученный флаг формата `flag{...}` платформе |
## Данные
| Параметр | Значение |
|----------|----------|
| Обычный пользователь | `demo` / `demo` |
| Уязвимый функционал | Раздел профиля «Bio Template» |
| Метод сохранения bio | `POST /profile/bio` (требует CSRF-токен из формы) |
| Источник флага | Переменная окружения процесса приложения |