Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2021-39115 — Injeção de Template em modelos de e-mail leva à execução de código no Jira Service Management Server | Kitploit
Ferramentas/GitHubGitHub/petrusviet/cve-2021-39115
Análise de VulnerabilidadesAnálise de CódigoExploraçãoExploração de Aplicações WebTestes de PenetraçãoAprendizado e Educação
GitHubpetrusviet/cve-2021-39115

CVE-2021-39115

Injeção de Template em modelos de e-mail leva à execução de código no Jira Service Management Server

Ver Repositório
48121há 5 anosRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2021-39115

Injeção de Template em Modelos de E-mail leva à execução de código no Jira Service Management Server

I) Build

Já mostrei como fazer deploy + debug aqui, vocês podem consultar.

II) Análise

Na Descrição deste CVE também está claro que o bug está no recurso Email Template. Com privilégios de admin, o usuário pode editar livremente os templates dos e-mails de notificação. Esse bug exige privilégios de admin para ser explorado, então mesmo com RCE não é tão grave, mas ainda assim decidi escrever o blog por diversão :) quem estiver estudando SSTI pode usar como referência.

  • Começando o trabalho, fiz deploy da versão atlassian-jira-servicedesk-4.17.0-m0006-standalone e tentei diff para ver as diferenças em relação à 4.18.0: image

Quando vi essa quantidade, ... é claro que não vou ler arquivo por arquivo, sou muito preguiçoso. Brincadeira, na verdade, além do patch de segurança, as versões também têm upgrades + mudanças de funcionalidades. Fazer diff de tudo e ficar lendo assim tomaria muito tempo. Claro que em alguns casos tenho que aturar o diff completo e ler, mas neste caso não fiz isso =)))

  • Como disse acima, o admin tem permissão para alterar o template de todos os e-mails de notificação. Portanto, o primeiro passo é saber quais contextos existem durante o parse dos e-mails de notificação; isso é especialmente importante quando se quer explorar um bug de SSTI (com sandbox). A maneira mais eficaz é debugar e entrar no trecho que faz o parse do template do e-mail para ver o valor da variável de contexto.
  • Claro que existem vários tipos de notificação (SignUp, change Password, ...), cada um usando um endpoint diferente. Usei o recurso SendBulkMail para criar o e-mail de notificação. Sobre a stack call desse endpoint, prefiro não mencionar para não ficar longo, pois é semelhante à stack call em CVE-2019-11581

Na função SimpleNote.render, posso ver a variável de contexto para ver o que dá para aproveitar:

Pela minha própria experiência, costumo prestar atenção e verificar contextos/classes com palavras-chave como: Utils, Manager, service, ... Notei que há o contexto $jirautils (classe com.atlassian.jira.util.JiraUtils) que contém o método public static <T> T loadComponent(String className, Class<?> callingClass) :

Fui vasculhar a documentação para entender a função dela e como usá-la, porque a entrada recebe uma String className e a saída é uma classe, então é bem provável que esse método carregue classes de forma arbitrária:

  • Como a documentação diz, essa função serve para carregar classes :) Comecei logo a verificar se o método realmente carregava uma classe arbitrária:

Entrei em System -> Email templates e baixei o arquivo de template atual para a máquina

Existem muitos templates diferentes, cada um usado para um tipo de notificação diferente, então procurei um arquivo que pudesse ser usado em comum por vários tipos de notificação, como email\html\includes\header.vm

Depois de enviar o arquivo de template modificado e usar SendBulkMail para disparar um e-mail de notificação, recebi a seguinte saída no e-mail:

Basicamente, essa classe não tem construtores ou não foi aceita. Tentei usar outra classe que tivesse construtores (construtores sem argumentos) para ver o que acontecia:

E o resultado foi exatamente o esperado. Consegui obter a classe com sucesso:

  • Então, já conseguimos carregar uma classe quase arbitrária, o que já é razoável. Mas como chegar ao RCE??? O Jira usa Velocity Template e tem uma sandbox bastante completa, com blacklist de pacotes e blacklist de classes (com a blacklist de classes, ela consegue bloquear todas as classes que estendem classes presentes na blacklist). Usar as classes públicas disponíveis na internet é quase impossível. Nesse ponto, fui obrigado a fazer diff entre a versão vulnerável e o patch para ver como diferiam; claro, não vou diff de tudo, apenas o arquivo de configuração do Velocity (\atlassian-jira\WEB-INF\classes\velocity.properties) para ver se havia algo novo/atualizado:

Vemos que 3 classes foram adicionadas à blacklist:

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

Ao verificar cada classe, percebi que a classe SpelExpressionParser tem o método public SpelExpression parseRaw(String expressionString); pesquisei no Google por um tempo para descobrir como usar e, basicamente, podia ser usado assim:

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

Como vocês podem ver, o método parseRaw retorna um objeto da classe org.springframework.expression.spel.standard.SpelExpression, mas ainda não renderiza de fato a expressão que informei. Entrei na classe SpelExpression e vi que existe o método 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;
    }

Olhando para a entrada/saída, alguém que joga no feeling como eu decide testar logo em vez de consultar a documentação de novo:

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

e o resultado:

III) Conclusão

Assim, conseguimos fazer bypass na sandbox do Velocity para obter RCE. Porém, ao me colocar no lugar do autor — a pessoa que encontrou esse bug — ainda há várias perguntas que não consigo explicar:

  • Por que foram adicionadas 3 classes à blacklist?

Quando investiguei essas 3 classes, vi que elas podem formar uma chain:

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

Se o autor tivesse usado $jirautils como eu, não precisaria das outras 2 classes. Mas, se o autor não usou $jirautils, como ele conseguiu obter o ComponentAccessor???

  • Como o autor conseguiu descobrir essas 3 classes? Essa é a pergunta que mais me intriga e que mais quero saber, mas talvez a única forma de saber seja entrando em contato direto com o autor. Infelizmente, não sei quem é o autor desse bug.

IV) Continuando o bypass do patch

Baixar ferramenta