Java代码审计之目录遍历漏洞详解
字数 1067 2025-08-11 22:57:21
Java代码审计之目录遍历漏洞详解
一、目录遍历漏洞原理
目录遍历漏洞(Path Traversal)是指通过用户输入参数,后端直接将参数拼接到指定路径下读取用户文件名时,由于对用户输入参数控制不严格,攻击者可以利用特殊字符跳转到服务器敏感目录,读取或操作敏感文件。
风险影响
- 读取服务器密码文件
- 访问程序数据库
- 获取Redis等核心配置文件
- 覆盖受保护的文件或目录
二、人工代码审计关键点
1. 审计关键字
需要重点关注以下Java关键字:
new FileInputStream(path)
new FileOutputStream(path)
new File(path)
RandomAccessFile fp = new RandomAccessFile(fname,"r");
mkdirs
getOriginalFilename
entry.getName()
2. 审计关键类和函数
sun.nio.ch.FileChannelImpl
java.io.File.list/listFiles
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileSystem/Win32FileSystem/WinNTFileSystem/UnixFileSystem
sun.nio.fs.UnixFileSystemProvider/WindowsFileSystemProvider
java.io.RandomAccessFile
sun.nio.fs.CopyFile
sun.nio.fs.UnixChannelFactory
sun.nio.fs.WindowsChannelFactory
java.nio.channels.AsynchronousFileChannel
FileUtil/IOUtil
filePath/download/deleteFile/move/getFile
3. 审计要点
- 检查对用户传递的文件对象/文件名/文件路径的处理
- 验证是否限制了可操作文件的路径、文件类型、文件所有者
- 检查是否排除了敏感文件
- 检查getPath()、getAbsolutePath()的路径判断逻辑
- 搜索安全策略配置文件中的权限设置:
- permission
- Java.io.FilePermission
- grant
三、常见漏洞代码示例
1. 未过滤文件路径
// 创建读取要拷贝的文件
InputStream inStream = new FileInputStream(file1);
// 创建要复制到的文件,filename未经校验
OutputStream inStream = new FileOutputStream(new File(file2+"\\"+filename));
2. 直接使用上传文件名
String orgName = mf.getOriginalFilename(); // 获取文件名
File file = new File(orgName);
file.mkdirs(); // 创建文件根目录
3. ZIP解压路径覆盖
// 开始解压
Enumeration entries = zipFile.entries();
// 遍历entries获得entry
while(entries.hasMoreElements()){
ZipEntry entry = (ZipEntry)entries.nextElement();
...
File targetFile = new File(entry.getName());
targetFile.getParentFile().mkdirs();
}
四、渗透测试方法
1. 常见测试方式
- 尝试使用路径跳转符:
../、..\、%2e%2e%2f、%2e%2e%5c、%20、%0a等 - 在Unix系统中尝试使用换行符:
etc/passwd%0a.jpg - Java特定绕过:
%c0%ae(解析为".") - 使用
%00截断符绕过白名单:WINDOWS/SchedLgU.txt%00.js
2. 测试示例
http://www.test.com/my.jsp?file=Windows/system.ini
http://www.test.com/my.jsp?file=%2e./...%2fWindows/system.ini
http://192.168.32.163/view.php?page=%20.etc/passwd
http://www.target.com/%c0%ae%c0%ae/%c0%ae%c0%ae/foo/bar
五、漏洞修复方案
1. 全局过滤关键字
private String fileNameValidate(String str) {
String fileNameListStr = "..,~,/,\\,%"; // 请求体中不能携带的关键字
if(null!=fileNameListStr && !"".equals(fileNameListStr)) {
str = str.toLowerCase();
String[] badStrs = fileNameListStr.split(",");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
return badStrs[i];
}
}
}
return null;
}
2. 正则表达式过滤
private static Pattern FilePattern = Pattern.compile("[\\s\\\\/:\\*\\?\\\"<>\\|]");
public static String filenameFilter(String str) {
return str==null?null:FilePattern.matcher(str).replaceAll("");
}
3. 白名单验证
public class CommUtils {
private static final String patternString = "^[a-zA-Z\\d-_\\.]+$";
private static final String patternString1 = "../,..\\,~,/,\\,%";
public static String filePathFilter(String filepath) {
final String[] split = patternString1.split(",");
for(String s : split) {
filepath = filepath.replace(s,"");
}
if(filepath.matches(patternString)) {
return filepath;
}
return null;
}
}
4. 严格输入验证
public class CleanPath {
public static String cleanString(String aString) {
if (aString == null) return null;
String cleanString = "";
for (int i = 0; i < aString.length(); ++i) {
cleanString += cleanChar(aString.charAt(i));
}
return cleanString;
}
private static char cleanChar(char aChar) {
// 0-9
for (int i = 48; i < 58; ++i) {
if (aChar == i) return (char) i;
}
// 'A'-'Z'
for (int i = 65; i < 91; ++i) {
if (aChar == i) return (char) i;
}
// 'a'-'z'
for (int i = 97; i < 123; ++i) {
if (aChar == i) return (char) i;
}
// 其他合法字符
switch (aChar) {
case '/': return '/';
case '.': return '.';
case '-': return '-';
case '_': return '_';
case ' ': return ' ';
}
return '%';
}
}
5. 受信任目录白名单
public static void main(String[] args) {
File file = new File(args[0]);
if (!isInSecureDir(file)) {
throw new IllegalArgumentException();
}
String canonicalPath = file.getCanonicalPath();
if (!canonicalPath.equals("/img/java/file1.txt") &&
!canonicalPath.equals("/img/java/file2.txt")) {
// 无效文件,处理错误
}
FileInputStream fis = new FileInputStream(f);
}
6. Web服务器配置
- IIS:删除可执行虚拟目录或关闭目录浏览
- Apache:修改httpd.conf文件,将"Options Indexes FollowSymLinks"中的"Indexes"删除
六、参考链接
- https://blog.csdn.net/qq_41085151/article/details/113525348
- https://www.cnblogs.com/zhangruifeng/p/16077916.html
- https://www.cnblogs.com/jayus/p/11423769.html
- https://www.cnblogs.com/macter/p/16181588.html
- ofcms环境搭建: https://forum.butian.net/share/1229