前言
Jira是Atlassian公司出品的项目与事务跟踪工具,被广泛应用于缺陷跟踪、客户服务、需求收集、流程审批、任务跟踪、项目跟踪和敏捷管理等工作领域。
近日,Jira 官方网站上公布了一个代号为 JRASERVER-69793 的 issue,CVE 编号 CVE-2019-8451,描述指出,Jira 的 /plugins/servlet/gadgets/makeRequest 资源存在 SSRF 漏洞,原因在于 JiraWhitelist 这个类的逻辑缺陷,攻击者通过此漏洞在未授权的情况下访问 Jira服务器所处的内网资源。
漏洞分析
根据描述,漏洞位于 /plugins/servlet/gadgets/makeRequest 页面,直接访问后返回 404

应该是该接口在访问上做了某些限制,查看 web.xml 后发现,所有 /plugins/servlet/* 链接都交由 ServletModuleContainerServlet 类处理了

找到 ServletModuleContainerServlet,反编译后发现它继承了父类的逻辑

其父类在 lib/atlassian-plugins-servlet-x.x.x.jar 中,其中 service 看起来应该是处理 http 请求的方法了。在函数首下断

请求 makeRequest 接口,成功断在了断点处,this.getServletModuleManager() 不为空,因此进入 else 代码块,单步调试。在 servlet 赋值处调用了 getPathInfo 方法获取了请求中的接口地址

返回结果作为参数传递给 getServlet 方法,该方法通过接口地址获取到了对应 Servlet 的描述信息,传递给 getServlet 方法去获取最终的 Servlet

描述信息中的 Servlet class 指向 com.atlassian.gadgets.shindig.servlet.XsrfMakeRequestServlet,反编译 XsrfMakeRequestServlet 类,查看其代码逻辑

可以看出,doGet 方法检查了请求头中是否存在 X-Atlassian-Token: no-check 项,不存在则返回 404 错误。这也就解释了为什么直接请求 makeRequest 接口会提示找到了死链接。
在请求头中添加 X-Atlassian-Token: no-check 后再次请求,页面返回 400 错误,提示缺少 url 参数

添加 url 参数,设置一个地址,请求后返回 403 错误,提示请求中的链接不被允许。

其实这里找到 JiraWhitelist 类下断就可以直接看出问题出在了哪,但还是追一下代码,梳理一下代码逻辑吧(实际操作时我是从 JiraWhitelist 逆着推的,这里正向写是为了方便理解)。
绕过 XSRF 验证之后,成功进入 else 分支,此时调用父类的 doGet 方法。

跟入 fetch,这个方法是用来生成响应的。传入的 request 通过 buildHttpRequest 构造完整的 HTTP 请求,之后传入 contentFetcherFactory.fetch 方法获取响应。继续跟入,寻找处理 request 的方法




这里对 response 进行了赋值,说明请求流程在 execute 方法中



这个 validateRequestTargetAgainstWhitelist 就是用来验证链接是否在白名单中的方法

跟进 whiltelist.allows 方法


uriString 的值是从请求中获取的,canonicalBaseUrl 的值是通过 getCanonicalBaseUrl() 方法获取到的 Jira 服务的请求链接。allows 函数在 return 位置做了或关系的断言,uriString.startsWith(canonicalBaseUrl) || this.whitelistService.isAllowed(uri),其中 startsWith 方法用于获取 url 中的 host。也就是说,只要满足 urlString 以 canonicalBaseUrl 开头或者 uri 在白名单中,allows 方法就会返回真,服务器则会正常请求这个 url。因此这里对第一个条件进行恶意构造,将请求中的 url 参数值以 Jira 服务的 host 作为开头,然后使用 @ 将请求指向其他地址,即可完成 SSRF 攻击。

参考
https://jira.atlassian.com/projects/JRASERVER/issues/JRASERVER-69793?filter=allissues
https://www.seebug.org/vuldb/ssvid-98074
作者: JenI 转载请注明出处,谢谢
Comments !