【Java代码审计】目录穿越篇
- 1.Java中的目录穿越
- 2.目录穿越漏洞审计
- 3.Java中目录穿越漏洞修复
1.Java中的目录穿越
目录穿越漏洞产生的本质是路径可控,一旦涉及文件的读取问题便会涉及java.io.File
类,因此在审计这类漏洞时可以优先查找java.io.File
引用,并根据经验来判断Paths、path、System.getProperty("user.dir")
等各类可能会用来构造路径的关键字
若项目采用Spring MVC这类框架也可以先查看一下路由,判断是否存在如path之类的路由
2.目录穿越漏洞审计
1、案例1:任意文件读取漏洞:
public String fileList(String filename) {String filePath = System.getProperty("user.dir") + "/logs/" + filename;StringBuilder sb = new StringBuilder();File f = new File(filePath);File[] fs = f.listFiles();if (fs != null) {for (File ff : fs) {sb.append(ff.getName()).append("<br>");}return sb.toString();}return filePath + "目录不存在!";
}
首先,使用System.getProperty("user.dir")
获取当前工作目录并拼接上"/logs/"和传入的文件名,得到路径filePath
代码中并没有对文件名进行验证或过滤,用户可以直接传入任意文件名来读取目录下的文件
例如,当我们传递参数为:../
发现可以读取上层目录的文件列表:
案例2:任意文件下载:
漏洞代码如下:
public String download(String filename, HttpServletResponse response) {String filePath = System.getProperty("user.dir") + "/logs/" + filename;try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)))) {response.setHeader("Content-Disposition", "attachment; filename=" + filename);response.setContentLength((int) Files.size(Paths.get(filePath)));response.setContentType("application/octet-stream");// 使用 Apache Commons IO 库的工具方法将输入流中的数据拷贝到输出流中IOUtils.copy(inputStream, response.getOutputStream());log.info("文件 {} 下载成功,路径:{}", filename, filePath);return "下载文件成功:" + filePath;} catch (IOException e) {log.error("下载文件失败,路径:{}", filePath, e);return "未找到文件:" + filePath;}
}
同样,这段代码没有对文件名进行验证或过滤,用户可以直接传入任意文件名来下载文件
例如,传入一个恶意的文件名:../../../../../../../etc/passwd
3.Java中目录穿越漏洞修复
对于目录穿越漏洞的防御相对简单,一般有以下方法:
- 对文件名进行过滤,防止出现“
./
”等特殊符号 - 采用ID索引的方法来下载文件,而不是直接通过文件名
- 对目录进行限制
- 合理配置权限
public String safe(String filename) {if (!Security.checkTraversal(filename)) {String filePath = System.getProperty("user.dir") + "/logs/" + filename;return "安全路径:" + filePath;} else {return "检测到非法遍历";}
}public static boolean checkTraversal(String content) {return content.contains("..") || content.contains("/");
}
此时再输入恶意的payload,检测到攻击行为: