Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2021-39115 — Template Injection in Email Templates leads to code execution on Jira Service Management Server | Kitploit
Tools/GitHubGitHub/petrusviet/cve-2021-39115
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & Education
GitHubpetrusviet/cve-2021-39115

CVE-2021-39115

Template Injection in Email Templates leads to code execution on Jira Service Management Server

View Repository
4812135 years agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2021-39115

Template Injection in Email Templates leads to code execution on Jira Service Management Server

I) Building

I have provided a guide for deployment + debug here, you can refer to it.

II) Analysis

The Description of this CVE already clearly states that the bug lies in the Email Template feature. With admin privileges, users can arbitrarily modify templates of notification emails. This bug requires admin privileges to execute, so even if it leads to RCE, it is not very critical, but I still decided to write this blog for fun :) those who are learning SSTI can refer to it.

  • Let's get down to business. I deployed version atlassian-jira-servicedesk-4.17.0-m0006-standalone and tried to diff it with version 4.18.0 to see what's different: image

When I saw this pile, ... of course I don't read each file one by one, I'm too lazy. Just kidding, actually besides the security fix, the versions also have upgrades and changes in features. If I diff everything and then sit down to read it, that's very time-consuming. Of course, in some cases I have to diff everything and read it, but for this case I didn't do that =)))

  • As mentioned above, an admin has the right to change the template of all notification emails. Therefore, the first step is to know what contexts exist during the parsing of notification emails. This is especially important when wanting to exploit an SSTI bug (which uses a sandbox). The most effective way is to debug and step into the email template parsing code to see the context variable values.
  • Of course, there are many different types of notifications (SignUp, change Password, ...), each type uses a different endpoint. I used the SendBulkMail feature to create a notification email. I will not mention the stack call of this endpoint to avoid being too lengthy, as it is similar to the stack call in CVE-2019-11581

At the SimpleNote.render function, I can look at the context variable to see what I can take advantage of:

Based on my own experience, I pay attention to checking contexts/classes that have keywords like: Utils, Manager, service, ... . I noticed a context $jirautils (class com.atlassian.jira.util.JiraUtils) contains the method public static <T> T loadComponent(String className, Class<?> callingClass) :

I wandered around looking for docs to read about its functionality and how to use it, because the input is a String className and the output is a class, so it is very likely that this method can load classes arbitrarily:

  • As the docs say, this function is used to load classes :) I immediately got to work to see if that method can actually load arbitrary classes:

I went to System -> Email templates and downloaded the current template file to my machine

There are many different templates, each used for a specific type of notification, so I looked for a file that can be shared across multiple notification types, such as email\html\includes\header.vm

After uploading the modified template file, and using SendBulkMail to send a notification email, I received the output in the email like this:

Basically, this class has no constructors or is not accepted. I tried using another class that has constructors (no-arg constructors) to see what happens:

And the result was as expected. I successfully obtained the class:

  • So we can now load classes almost arbitrarily, which is quite good. But how can we achieve RCE ??? Jira uses Velocity templates and has a fairly complete sandbox, with blacklist packages and blacklist classes (with blacklist classes it can block all classes that extend from those in the blacklist). Using classes publicly available on the internet is nearly impossible. At this point, I had to diff between the vulnerable version and the patched version to see how they differ. Of course, I won't diff everything; I will only diff the Velocity conf file (\atlassian-jira\WEB-INF\classes\velocity.properties) to see if anything new was updated:

We see that 3 classes were added to the blacklist:

org.springframework.expression.spel.standard.SpelExpressionParser,\
com.atlassian.jira.component.ComponentAccessor,\
com.atlassian.jira.plugin.ComponentClassManager

When checking each class, I noticed that the SpelExpressionParser class has the method public SpelExpression parseRaw(String expressionString). After a quick Google search to find how to use it, basically it can be used like this:

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

As you can see, the parseRaw function returns an object of the class org.springframework.expression.spel.standard.SpelExpression, but it hasn't actually rendered the expression I provided. I looked into the SpelExpression class and found the method getValue:

@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]);
                }
            }
Download Tool