文件访问类漏洞
通常把这类漏洞归为一个类型,因为产生漏洞的原因都是因为程序对文件或目录访问控制不严、程序内部逻辑错误导致的任意文件或目录恶意访问漏洞。
1. 任意文件读取
任意文件读写漏洞即因为没有验证请求的资源文件是否合法导致的,此类漏洞在Java中有着较高的几率出现,任意文件读取漏洞原理很简单,但一些知名的中间件:Weblogic、Tomcat、Resin又或者是主流MVC框架:Spring MVC、Struts2都存在此类漏洞。
示例 - 存在恶意文件读取漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.File" %> <%@ page import="java.io.FileInputStream" %> <%@ page import="java.io.IOException" %>
<pre> <% String fileName = request.getParameter("name"); String basePath = application.getRealPath("/"); File file = new File(basePath, fileName);
if (!file.exists() || !file.isFile()) { out.println("文件不存在"); return; }
FileInputStream in = null; try { in = new FileInputStream(file); int tempbyte; while ((tempbyte = in.read()) != -1) { out.write(tempbyte); } } catch (IOException e) { out.println("读取文件时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } %> </pre>
|
1.1 目录任意文件读取漏洞测试
攻击者通过传入恶意的name参数可以读取服务器中的任意文件:http://localhost:8080/s1/?name=index.jsp,如下图:

2. 写文件
示例 - 存在恶意文件写入漏洞的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.File" %> <%@ page import="java.io.FileOutputStream" %> <%@ page import="java.io.IOException" %>
<% String fileName = request.getParameter("f"); String content = request.getParameter("c");
if (fileName == null || fileName.trim().isEmpty() || content == null) { out.println("错误:文件名和内容参数不能为空"); return; }
String baseDir = application.getRealPath("/"); File file = new File(baseDir, fileName); FileOutputStream fos = null;
try { fos = new FileOutputStream(file); fos.write(content.getBytes("UTF-8")); fos.flush();
out.println("文件路径:" + file.getAbsolutePath() + "<br>"); out.println("文件是否存在:" + file.exists()); } catch (IOException e) { out.println("写入文件时发生错误:" + e.getMessage()); e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } %>
|
2.1 跨目录写入文件测试

3. 删除文件
3.1 任意文件删除测试
示例 - 存在任意文件删除漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.File" %> <%@ page import="java.io.IOException" %>
<% String fileName = request.getParameter("file");
if (fileName == null || fileName.trim().isEmpty()) { out.println("错误:文件参数不能为空"); return; }
String baseDir = application.getRealPath("/"); File targetFile = new File(baseDir, fileName);
if (!targetFile.exists()) { out.println("错误:文件不存在 - " + targetFile.getAbsolutePath()); return; }
try { boolean isDeleted = targetFile.delete(); if (isDeleted) { out.println("成功:文件已删除 - " + targetFile.getAbsolutePath()); } else { out.println("失败:无法删除文件 - " + targetFile.getAbsolutePath()); } } catch (SecurityException e) { out.println("错误:没有权限删除文件 - " + e.getMessage()); e.printStackTrace(); } %>
|
攻击者通过参入file参数即可删除服务器中的任意文件:

3.2 FileSystem任意文件删除测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.File" %> <%@ page import="java.lang.reflect.Method" %>
<% String file = request.getParameter("file");
Method m = Class.forName("java.io.DefaultFileSystem").getMethod("getFileSystem"); m.setAccessible(true); Object fs = m.invoke(null);
Method m2 = fs.getClass().getMethod("delete", File.class); m2.setAccessible(true); out.print(m2.invoke(fs, new File(file))); %>
|
攻击者通过反射调用 Filesystem 并执行delete方法,用来绕过对 File 对象 delete方法的防御。
4. 文件/目录复制、移动
示例 - 存在任意文件复制漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.IOException" %> <%@ page import="java.nio.file.Files" %> <%@ page import="java.nio.file.Path" %> <%@ page import="java.nio.file.Paths" %> <pre> <% try { Path path = Files.copy(Paths.get(request.getParameter("source")), Paths.get(request.getParameter("dest")));
out.println(path); } catch (IOException e) { e.printStackTrace(); } %> </pre>
|
攻击者传入恶意的source和dest参数可以实现复制任何文件到任意的目录,比如攻击者可以在用户中心上传一张内容为WebShell恶意代码的1.jpg图片文件,然后通过漏洞将1.jpg图片文件,复制到同级目录并更新名称为1.jsp的可解析脚本文件,访问1.jsp文件即可实现控制服务器的目的
在实际环境中,应用系统可能根据需求在配置文件如web.xml中或代码层面如filter设置某些目录(如上传目录、资源目录等)禁止对 .jsp 脚本文件等可执行文件进行解析,因此,攻击者需要将恶意文件移动或复制到其他能够执行的目录进行解析。
5. 重命名文件
示例 - 存在文件名重命名漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.File" %> <% String fileName1 = request.getParameter("s"); String fileName2 = request.getParameter("d");
File f = new File(fileName1); File d = new File(fileName2);
f.renameTo(d);
out.println(d + "\t" + d.exists()); %>
|
攻击者传入恶意的s和d参数即可将文件名为1.txt的文本文件重命名为1.jsp可执行脚本文件,请求:http://localhost:8000/modules/filesystem/file-rename.jsp?s=/tmp/1.txt&d=/tmp/1.jsp
攻击者会使用重命名的方式将(txt、jpg等资源文件)重命名为可执行脚本文件(jsp)来获得webshell从而控制Web应用系统,并绕过某些安全防护机制。常见的攻击手段是在文件上传时,上传包含恶意代码的图片文件,再利用重命名将其转为可执行的脚本文件。