Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2021-39115 — Инъекция в шаблонах электронных писем приводит к выполнению кода на сервере Jira Service Management | Kitploit
Инструменты/GitHubGitHub/petrusviet/cve-2021-39115
Анализ уязвимостейАнализ КодаЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеОбучение и Образование
GitHubpetrusviet/cve-2021-39115

CVE-2021-39115

Инъекция в шаблонах электронных писем приводит к выполнению кода на сервере Jira Service Management

Репозиторий
481215 лет назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2021-39115

Инъекция шаблонов в Email Templates приводит к выполнению кода на Jira Service Management Server

I) Сборка

Я уже рассказывал, как развернуть и отладить всё это здесь, можете почитать.

II) Анализ

В описании этого CVE прямо сказано, что баг находится в функциональности Email Template. Имея права администратора, пользователь может произвольно изменять шаблоны писем-уведомлений. Баг требует прав администратора, поэтому даже RCE не слишком критичен, но я всё равно решил написать блог, просто ради удовольствия :) тем, кто изучает SSTI, может пригодиться.

  • Приступая к делу, я развернул сборку atlassian-jira-servicedesk-4.17.0-m0006-standalone и решил сделать diff, чтобы посмотреть, чем она отличается от версии 4.18.0: image

Увидев этот ворох файлов,.... конечно, я не стал читать каждый файл по отдельности — я слишком ленив. Шучу. На самом деле, помимо добавленного фикса безопасности, в версиях есть и обновления/изменения функциональности. Сидеть и читать всё после полного diff — слишком долго. Разумеется, в некоторых случаях приходится делать полный diff и разбирать его, но в данном случае я так делать не стал =)))

  • Как уже было сказано, администратор может менять шаблоны всех email-уведомлений. Поэтому первым шагом нужно понять, какие переменные context доступны при разборе уведомлений. Это особенно важно при попытке эксплуатировать SSTI (когда используется песочница). Самый эффективный способ — отладить участок разбора шаблона письма и посмотреть значения переменных context.
  • Разумеется, типов уведомлений много (SignUp, смена пароля,...), и у каждого типа свой endpoint. Я использовал функцию SendBulkMail, чтобы сформировать письмо-уведомление. Стек вызовов этого endpoint я, пожалуй, описывать не буду, чтобы не растягивать статью, — он аналогичен стеку вызовов в CVE-2019-11581

В функции SimpleNote.render я могу посмотреть переменную context, чтобы понять, что можно использовать:

По своему опыту я обращаю внимание на контексты/классы, в названиях которых есть такие ключевые слова, как Utils, Manager, service и т.п. Я заметил контекст $jirautils (класс com.atlassian.jira.util.JiraUtils), у которого есть метод public static <T> T loadComponent(String className, Class<?> callingClass) :

Я поискал документацию и почитал, что делает этот метод и как его использовать, ведь на вход он принимает String className, а на выходе возвращает класс, так что вполне возможно, что с его помощью можно загружать классы произвольно:

  • Как и сказано в документации, эта функция нужна для загрузки классов :) Я сразу принялся проверять, действительно ли этот метод может загрузить произвольный класс:

Я захожу в System -> Email templates и скачиваю текущий файл шаблона на машину

Шаблонов много, и каждый используется для своего типа уведомлений, поэтому я искал файл, который можно использовать сразу для нескольких типов, например email\html\includes\header.vm

После того как я загрузил изменённый шаблон и с помощью SendBulkMail отправил письмо-уведомление, я получил в письме такой вывод:

Похоже, у этого класса нет конструкторов, либо он не принимается. Я попробовал другой класс, у которого есть конструктор (без входных параметров), и посмотрел, что получится:

И результат оказался именно таким, как я ожидал. Класс получить удалось:

  • Итак, мы можем загружать классы почти произвольно — это уже неплохо. Но как получить RCE ??? Jira использует velocity-шаблоны с довольно полной песочницей: есть чёрный список пакетов и чёрный список классов (при этом чёрный список классов блокирует и все классы, унаследованные от классов из списка). Использовать классы, публикуемые в интернете, практически невозможно. Поэтому мне пришлось сделать diff между уязвимой и пропатченной версиями, чтобы понять, чем они отличаются. Разумеется, я не стал диффать всё, а посмотрел только конфигурационный файл velocity (\atlassian-jira\WEB-INF\classes\velocity.properties) — не появилось ли там чего-то нового:

Видно, что в чёрный список добавили 3 класса:

root@kitploit:~
org.springframework.expression.spel.standard.SpelExpressionParser,\
com.atlassian.jira.component.ComponentAccessor,\
com.atlassian.jira.plugin.ComponentClassManager

Проверив каждый класс, я заметил, что у класса SpelExpressionParser есть метод public SpelExpression parseRaw(String expressionString). Я немного погуглил, как им пользоваться, и в целом его можно использовать так:

root@kitploit:~
#set($SpelExpressionParser = $jirautils.loadComponent('org.springframework.expression.spel.standard.SpelExpressionParser',$i18n.getClass()))
$SpelExpressionParser.parseRaw("T(java.lang.Runtime).getRuntime().exec('calc')")

Как видите, функция parseRaw возвращает объект класса org.springframework.expression.spel.standard.SpelExpression, но само переданное выражение ещё не вычисляется. Я заглянул в класс SpelExpression и увидел метод getValue:

root@kitploit:~
@Nullable
    public Object getValue() throws EvaluationException {
        CompiledExpression compiledAst = this.compiledAst;
        if (compiledAst != null) {
            try {
                EvaluationContext context = this.getEvaluationContext();
                return compiledAst.getValue(context.getRootObject().getValue(), context);
            } catch (Throwable var4) {
                if (this.configuration.getCompilerMode() != SpelCompilerMode.MIXED) {
                    throw new SpelEvaluationException(var4, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION, new Object[0]);
                }
            }

            this.compiledAst = null;
            this.interpretedCount.set(0);
        }

        ExpressionState expressionState = new ExpressionState(this.getEvaluationContext(), this.configuration);
        Object result = this.ast.getValue(expressionState);
        this.checkCompile(expressionState);
        return result;
    }

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

root@kitploit:~
#set($SpelExpressionParser = $jirautils.loadComponent('org.springframework.expression.spel.standard.SpelExpressionParser',$i18n.getClass()))
$SpelExpressionParser.parseRaw("T(java.lang.Runtime).getRuntime().exec('calc')").getValue()

и результат:

III) Заключение

Итак, песочницу velocity обойти удалось и получить RCE. Но если поставить себя на место автора — человека, нашедшего этот баг, — остаётся много вопросов, на которые я не могу ответить:

  • Почему в чёрный список добавили целых 3 класса?

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

root@kitploit:~
ComponentAccessor.getComponentClassManager() 
  => ComponentClassManager.newInstance(String className) (Class này có thể load class tùy ý)
        => SpelExpressionParser

Если автор использовал $jirautils, как и я, то два других класса ему не нужны. Но если автор не использовал $jirautils, то как он получил ComponentAccessor???

  • Как автор вообще смог найти эти три класса? Этот вопрос интересует меня больше всего, но, пожалуй, узнать это можно только напрямую связавшись с автором. К сожалению, я не знаю, кто автор этого бага.

IV) Продолжаем обходить пропатченную версию

Скачать инструмент