Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2019-11581 — Atlassian Jira 未认证模板注入 | Kitploit
工具/GitHubGitHub/petrusviet/cve-2019-11581
漏洞分析漏洞利用ShellcodeWeb应用程序漏洞利用学习与教育Payload 开发
GitHubpetrusviet/cve-2019-11581

CVE-2019-11581

Atlassian Jira 未认证模板注入

查看仓库
62704年前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Atlassian Jira 未认证模板注入 (CVE-2019-11581)

I) 搭建环境

1. 漏洞版本

4.4.x
5.x.x
6.x.x
7.0.x
7.1.x
7.2.x
7.3.x
7.4.x
7.5.x
7.6.x 至 7.6.14(7.6.x 的修复版本)
7.7.x
7.8.x
7.9.x
7.10.x
7.11.x
7.12.x
7.13.x 至 7.13.5(7.13.x 的修复版本)
8.0.x 至 8.0.3(8.0.x 的修复版本)
8.1.x 至 8.1.2(8.1.x 的修复版本)
8.2.x 至 8.2.3(8.2.x 的修复版本)

2. 构建及调试

  • 修改 ./bin/setenv.bat 文件中的 set JVM_SUPPORT_RECOMMENDED_ARGS= 值(或 Linux 下对应的 ./bin/setenv.sh),以启用远程调试
set JVM_SUPPORT_RECOMMENDED_ARGS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
  • 在 IDE(Intellij)中创建一个调试配置:Remote JVM Debug,host 和 port 设置为 localhost:5005,Command line arguments for remote JVM 为:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
  • 运行 ./bin/config.bat(或 Linux 下的 config.sh),将 "Jira home" 设置为自己的 jira home 目录 image

  • 运行 ./bin/start-jira.bat(或 Linux 下的 ./bin/start-jira.sh)。请检查 Java 版本以及 8080 端口和 5005 端口是否可用,否则无法启动!

  • 选择 private 模式 image

  • 到这一步,请记得使用能接收邮件的邮箱地址 :) image

  • 配置并测试连接,确保完整 image

  • 在 http://localhost:8080/secure/admin/EditApplicationProperties!default.jspa 开启 Contact Administrators Form 功能 image

  • 我只记录了一些构建时的注意事项,更多可参考 从源码构建 Jira

II) 分析

阅读 安全公告 后,我们知道此漏洞位于 ContactAdministrators 和 SendBulkMail(后者需要认证,故略过)。因此我开始测试 ContactAdministrators 功能并抓取请求。

image

  • 我看到请求发往 /secure/ContactAdministrators.jspa,于是查看文件 ./atlassian-jira/WEB-INF/web.xml 来确定该请求被转发到哪个类
    image
    image

  • 因此该请求将由 JiraWebworkActionDispatcher 处理,我在该类的 init 和 server 方法上设置断点并启动调试 image

  • 程序停在了 server 方法,跟踪一会儿后程序跳入 ContactAdministrators.doExecute() image

  • 然后进入 send,在此方法中程序列出了所有活跃的管理员账户 image

  • 接着程序进入 sendTo 方法。这里我们看到程序创建了一个 MailQueueItem 并将其添加到 mailQueue 中。 image

  • 程序调用了 EmailBuilder.withSubject。此处邮件主题(攻击者提交)字符串被转换为 TemplateSources 并赋值给 EmailBuilder 的 subjectTemplate 参数。 image

  • 在 renderLater 方法中,程序创建了一个 EmailRenderer 并用它来生成 RenderingMailQueueItem image

  • 此时,回到 ContactAdministrators.sendTo。创建完 MailQueueItem 后,程序将其加入 mailQueue 并返回 doExecute 进行重定向。如果按照这个调试流程,我们无法跳转到邮件渲染部分,也就无法到达模板注入发生的位置。那么如何跟踪邮件的处理过程呢?

  • 我看到 EmailRenderer 中有 renderNow 方法(程序只调用了 renderLater),我猜测当队列中的邮件被调用渲染时,也会走类似 renderNow 的流程。因此我决定从 renderNow 方法开始跟踪。 image

  • 从 renderNow 出发,程序调用 EmailRenderer.render()。我在此处设置断点并重新发起请求,以确认程序是否确实执行到这里。 image

  • 幸运的是,程序按照我预想的方向执行了。接下来调用 renderEmailSubject image

  • 接着调用 DefaultVelocityTemplatingEngine.render(this.subjectTemplate) image

  • 进入 DefaultVelocityTemplatingEngine.applying 和 DefaultVelocityTemplatingEngine.asPlainText image

  • 继续调用 asPlainText(Writer writer) image

  • 进入 toWriterImpl,由于传入的 writer 是一个 Fragment,程序跳转到 else 分支

private void toWriterImpl(Writer writer, boolean attachCartridge) throws IOException {
            if (this.source instanceof File) {
                File template = (File)this.source;
                if (attachCartridge) {
                    this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
                }

                DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBody(writer, template.getPath(), "", DefaultVelocityTemplatingEngine.this.applicationProperties.getEncoding(), this.context);
            } else if (this.source instanceof Fragment) {
                Fragment fragment = (Fragment)this.source;
                if (attachCartridge) {
                    this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
                }

                DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBodyForContent(writer, fragment.getContent(), this.context);
            }

        }
  • 然后调用 DefaultVelocityManager.writeEncodedBodyForContent image
  • 继续经过 VelocityEngine.evaluate -> RuntimeInstance.evaluate(Context, Writer, String, String) -> RuntimeInstance.evaluate(Context, Writer, String, Reader)。在此处,程序从 Reader 创建了一个 SimpleNode image
  • 程序执行到 render 方法,此处调用了 nodeTree.render(ica, writer); 来解析 Velocity 模板,因此这里可以发生模板注入!
public boolean render(Context context, Writer writer, String logTag, SimpleNode nodeTree) throws IOException {
        InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context);
        ica.pushCurrentTemplateName(logTag);

        try {
            try {
                nodeTree.init(ica, this);
            } catch (TemplateInitException var13) {
                throw new ParseErrorException(var13);
            } catch (RuntimeException var14) {
                throw var14;
            } catch (Exception var15) {
                String msg = "RuntimeInstance.render(): init exception for tag = " + logTag;
                this.getLog().error(msg, var15);
                throw new VelocityException(msg, var15);
            }
下载工具