作者:W3Qr
任意文件上传示例
public class FileUploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //判断请求是否为multipart请求 if(!ServletFileUpload.isMultipartContent(request)){ throw new RuntimeException("当前请求不支持文件上传"); } try { /* DiskFileItemFactory可以设置缓存大小以及临时文件保存的位置 临时文件默认村粗在系统的临时文件目录下 */ DiskFileItemFactory factory = new DiskFileItemFactory(); /* ServletFileUpload(factory)创建一个上传工具,指定使用缓存区与零食文件存储位置 */ ServletFileUpload servletFileUpload = new ServletFileUpload(factory); //解析请求,获取到所有的item,每一个item相当于一个上传项 List<FileItem> items = servletFileUpload.parseRequest(request); //遍历items for(FileItem item : items){ //isFormField,True表示是普通表单项,false为文件表单项 if(item.isFormField()){ //若item为普通表单项 String fieldName = item.getFieldName(); //获取表单项名称 String fieldValue = item.getString(); //获取表单项的值 System.out.println(fieldName + " = " + fieldValue); }else{ //若item为文件表单项 //获取上传文件原始名称 String fileName = item.getName(); //获取输入流,其中有上传 文件的内容 InputStream is = item.getInputStream(); //获取文件保存在服务器的路径 String path = this.getServletContext().getRealPath("/images"); //创建目标文件,将用来保存上传文件 File file = new File(path, fileName); //将输入流中的数据写入到输出流中 OutputStream os = new FileOutputStream(file); //将输入流中的数据写入输出流中 int len = -1; byte[] buf = new byte[1024]; while((len = is.read(buf)) != -1){ os.write(buf, 0, len); } os.close(); is.close(); } } } catch (FileUploadException e) { e.printStackTrace(); } } } |
几种修复方式
String fileType= ""; int i = fileName.lastIndexOf('.'); if (i > 0) { fileType= fileName.substring(i+1); } //... if("jpg".equals(fileType) || "png".equals(fileType) ....){ //your code } |
通过文件头校验
// 获得文件头部字符串 public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } FILE_TYPE_MAP.put("jpg", "FFD8FF"); //JPEG FILE_TYPE_MAP.put("png", "89504E47"); //PNG FILE_TYPE_MAP.put("gif", "47494638"); //GIF |
通过ImageIO判断
/** * 通过读取文件并获取其width及height的方式,来判断判断当前文件是否图片,这是一种非常简单的方式。 * @param imageFile * @return */ public static boolean isImage(File imageFile) { if (!imageFile.exists()) { return false; } Image img = null; try { img = ImageIO.read(imageFile); if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) { return false; } return true; } catch (Exception e) { return false; } finally { img = null; } } |
任意文件下载漏洞代码审计
//服务端 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String file = request.getParameter("filename"); String dpath = request.getParameter("path"); ServletContext con = request.getServletContext(); String type = file.substring(file.lastIndexOf(".")+1); System.out.println(type); System.out.println(type=="php"); System.out.println(type.equals("php")); if (!type.equals("php")){ String path = con.getRealPath("/")+dpath+file; System.out.println(path); FileInputStream stream = new FileInputStream(path); String mime = con.getMimeType(path); response.setHeader("content-type",mime); response.setHeader("content-disposition","attachment;filename="+file); byte[] bytes = new byte[1024]; int len=0; ServletOutputStream out = response.getOutputStream(); while ((len=stream.read(bytes))!=-1){ out.write(bytes); } }else { System.out.println("文件类型不允许"); } } |
此处替换文件名为“WEB-INF/web.xml”,下载系统配置文件:
综上所述文件下载漏洞的成因是由于未对用户传输的路径做过滤
任意文件下载漏洞修复
1、漏洞修复可以根据自身业务需要修改,大致修复方法如下:
对文件下载进行过滤,过滤掉“./”、“../”、“%”等,代码如下:
但是可以构造完整路径,下载任意文件:
2、对下载的文件路径进行严格控制,只允许下载某部分目录下的文件:
但仍然可以被绕过,在文件名那里构造即可
3、对下载文件后缀名做严格控制
原文始发于微信公众号(安全宇宙):【创宇小课堂】任意文件上传漏洞代码审计
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论