1. 威客安全首页
  2. 安全资讯

原创干货 | Java代码审计之任意文件下载

通过超链接下载文件时,如果浏览器可以识别该文件格式(txt、jpg等),浏览器就会直接打开。只有浏览器不能识别该文件格式的时候,才会实现下载。否则前端页面通过超链接方式发起文件下载请求,把要下载的文件名传递给后台,在对应的后台接口实现文件的下载操作(设置响应类型及响应头,输出流写入文件内容)。

以下是通过Servlet程序实现下载的过程:

1.通过File对象进行文件的封装,然后通过输入流InputStream将文件中的数据读取到java程序中;

2.通过setContentTpye方法设置Content-Type字段的值,设置为application/octet-stream或application/x-msdownload,决定客户端服务器以哪种方式来接受返回的信息;

application/octet-stream:文件拓展名.*(二进制流)application/x-msdownload:文件拓展名.dll(告诉浏览器这是一个要保存到本地的下载文件)

3.通过HttpServletResponse.setHeader方法设置Content-Disposition的值为”attachment:filename=文件名”,浏览器通过附件的形式来获取到用户上传的文件;

4.读取下载文件,通过OutputStream方法使用输出流将刚刚封装在输入流的文件内容向客户端写入。



双层定位文件下载


常见业务关键字

  • download

  • fileName

  • filePath

  • write

  • getFile

  • getPath

  • getWriter

相关操作类

  • InputStream

  • File

  • OutputStreaam

  • BufferedInputStream

  • FileInputStream

除了上述方法以外,还可以直接定位源代码中的工具类,为了降低耦合,文件操作相关的代码都会通过封装成一个工具类,不通的需求会通过重载或者重写进行实现。

例如以下关键字:

  • FileUtil

  • IOUtil



审计要点


通过相关方法定位到关键字后,一般检查点如下:

  • 路径相关参数是否用户可控

  • 是否限定了可下载的文件目录范围

  • 是否配置了全局过滤器或者额外进行了路径处理(例如过滤“…/”和“/”等)(如果使用了过滤器,需要检查数据获取方式、规则、顺序等)



相关案例


相关代码如下:

import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class DownloadServlet extends HttpServlet {   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {     // TODO Auto-generated method stub     //定义文件下载路径     String path =getServletContext().getRealPath("/")+"image/";     //获取要下载的文件名称     String filename = request.getParameter("filename");     File file = new File(path+filename);     if(file.exists()) {       //实现文件的下载       //设置响应类型application/octet-stream       response.setContentType("application/x-msdownload");       //设置头信息(以附件形式打开)       response.setHeader("Content-Disposition", "attachment;filename=""+filename+""");       //读取要下载的文件       InputStream inputStream = new FileInputStream(file);       ServletOutputStream outputStream = response.getOutputStream();       byte b[] =new byte[1024];       int n;       while((n = inputStream.read(b))!=-1) {         outputStream.write(b,0,n);       }       //关闭流,释放资源       outputStream.close();       inputStream.close();     }else {       request.setAttribute("errorResult", "文件不存在下载失败!");       //重定向       RequestDispatcher dispatcher = request.getRequestDispatcher("jsp/Filedownload.jsp");       dispatcher.forward(request, response);     }   }   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {     // TODO Auto-generated method stub     doGet(request, response);   }}
首先从客户端获取filename,然后拼接路径封装到File对象中:
Stringfilename = request.getParameter(“filename”);
Filefile = new File(path+filename);
然后将File对象转换成字节流,通过OutputStream输出流输出到客户端中。整个过程没有对目录穿越符号…/或者下载的路径进行处理限制。假设传入的filename参数为…/…/…/…/…/…/…/…/…/…/etc/passwd即可穿越路径达到任意文件下载的效果。



具体修复



  • 对用户输入的数据进行过滤,过滤掉”./”、”…/”、”%”、”/”

  • 对文件类型进行白名单控制,可以限定允许下载的文件后缀

  • 文件路径保存至数据库,让用户提交文件名对应ID进行文件下载(注意SQL注入问题)

//获取要下载的文件名称String filename =SqlQuery.PathURL(request.getParameter("id"));//通过id从数据库获取文件的值
  • 公开文件可使用超链接方式放置在web应用程序下载目录中通过链接直接下载即可

  • 使用file.getCanonicalPath()判断文件的真实路径,仅允许下载规定路径下的文件.对于getCanonicalPath()函数,“.”就表示当前的文件夹,而”…“则表示当前文件夹的上一级文件夹,函数处理返回的就是将符号完全解析的完整路径。


云众可信

微信号 : yunzhongkexin


来云众,玩不同


长按识别二维码关注

原创干货 | Java代码审计之任意文件下载


我就知道你在看!

原文始发于微信公众号(云众可信):原创干货 | Java代码审计之任意文件下载

本文转为转载文章,本文观点不代表威客安全立场。

发表评论

登录后才能评论

联系我们

15110186328

在线咨询:点击这里给我发消息

邮件:zhanglei@jinlongsec.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code
X