# 一、漏洞环境信息

http://127.0.0.1:8080/fileread/read?file=1.txt

# payload: http://127.0.0.1:8080/fileread/read?file=../../../../../../../../etc/pass

# 二、修复说明

# 分析service层 fileRead(String filePath) 添加修复代码前后分析

文件读取核心代码

    @Override
    public String fileRead(String filePath) {
        //1.获取文件路径
        String staticPath = ContextLoader.getCurrentWebApplicationContext().getServletContext().getRealPath("/static");

        //安全检验
        Boolean fileInDir = fileReadCheck.isFileInDir(filePath, new File(staticPath));
        if (fileInDir==false){
            return null;
        }

        //2.得到需读取的文件完整路径
        File filepath = new File(staticPath+File.separator+filePath);

        //3.判断文件是否存在
        if(filepath.isFile()==false){
            return null; //表示不存在该文件,直接返回空
        }

        //3.读取文件内容并返回
        String sb = null;
        try {
            sb = readAction(filepath); //读取文件代码
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb;
    }

    @Override
    public String readAction(File file) throws IOException {
        略.
    }

文件读取校验代码

/*
任意文件读取过滤函数
* */
public class fileReadCheck {
    //判断字符串格式的文件名是否在目录中存在
    static public Boolean isFileInDir(String fileName, File dir){
        if(dir.isDirectory()==false){
            return false;
        }
        //1。获取目录下的所有文件名
        File[] files = dir.listFiles();

        for (int i = 0; i < files.length; i++) {
            if (fileName.equals(files[i].getName())){ //如果需要读取的目录中存在和读取的文件名相同则返回true
                return true;
            }
        }
        return false; //没有找到目标相同的文件名,返回false
    }
}

修复说明

  • 获取需要读取文件的目录下的所有文件名;
  • 比对目录下所有文件名和需要读取的文件名
  • 若有相同的文件名,读取 否->拒绝读取操作

# 三、小结

  • 成因:输入未校验
  • 位置:Service层
  • 修复:ESAPI编码/逻辑处理修复
    • ESAPI分析:正常的文件名称由字母数字组成,另外上传的合法文件应由系统重新生成唯一的id去命名,因此可以粗暴的使用ESAPI的encodeForOS编码处理
    • 逻辑修复分析:filePath变量无论是否带有路径穿越的路径信息,对于系统来说都只是一个字符串,可以在需要读取的目录中去比对文件名是否一致来处理,从而限制了目录穿越带来的文件读取操作