Java代码审计:文件篇/文件上传/文件读取/目录遍历

0x00 前提

自学Java 代码审计,主要自己一个人学习,有点闭门造车,搜索引擎学习法,但是还是记录一下,也分享一下,也便于将来的总结和反思,如果我能终能学到什么,我也会重新梳理思路,为那些自学者提供一个好的思路,所以有了下面的系列文章java代码审计自学篇。

0x01 文件路径穿越

简述:

  • 许多的文件漏洞都是来源于文件路径的问题,好多时候也是路径可控,再加上一下程序员奇怪的逻辑。
  • 如果漏洞路径可控提供很多其他突破的方法
  • 攻击者利用../可以上传至任意指定目录或者目录穿越。

示例代码:

中间有../可以造成文件路径的不安全

package file;

import java.io.File;
import java.io.IOException;

public class filepath {
    public static void main(String[] args) throws IOException {
        File file = new File("../../file/123.txt");
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getCanonicalPath());
        System.out.println(file.exists());
    }
}

Java代码审计:文件篇/文件上传/文件读取/目录遍历_第1张图片

潜在的目录穿越:

一个文件被打开,然后读取文件内容,这个文件名来自于一个输入的参数。如果没有过滤这个传入的参数,那么本地文件系统中任意文件都会被读取。

文件读取有问题,别在里面拼接
val result = Source.fromFile("public/" + value).getLines().mkString // Weak point

修复:要在外面拼接好
filename = "public/" + FilenameUtils.getName(value)
val result = Source.fromFile(filename).getLines().mkString 

0x02文件上传

简述:

文件上传过程中,因为未校验上传文件后缀类型,导致用户可上传jsp和jspx等一些webshell文件。

代码审计时可重点关注对上传文件类型是否有足够安全的校验。

漏洞示例:

没有任何过滤

jdk原始的流操作上传

public String FileUpload(MultipartFile file){
        String fileName = file.getOriginalFilename();
        if (fileName==null) {
            return "file is error";
        }
  			//目录拼接
        String filePath = "/static/images/uploads/"+fileName;
        if (!file.isEmpty()) {
            try {
              	//转化字节流
                byte[] bytes = file.getBytes();
              	//创建file对象 转化为BufferedOutputStream对象
                BufferedOutputStream stream =
                        new BufferedOutputStream(new FileOutputStream(new File(filePath)));
                //写入流  
                stream.write(bytes);
                //关闭流
                stream.close();
                return "OK";
            } catch (Exception e) {
                return e.getMessage();
            }
        } else {
            return "You failed to upload " + file.getOriginalFilename() + " because the file was empty.";
        }
    }

框架常用的封装类上传

private static String UPLOADED_FOLDER = "/tmp/";

public String FileUpload(@RequestParam("file") MultipartFile file,RedirectAttributes redirectAttributes) {
        //检测文件是否存
        if (file.isEmpty()) {
            // 赋值给uploadStatus.html里的动态参数message
            redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
            return "redirect:/file/status";
        }

        try {
            // 获取文件,上传
          	// 获取字节流,放入数字
            byte[] bytes = file.getBytes();
             //获取文件路径,目录拼接  	/TMP/ + Filename
            Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
          	 //文件写入
            Files.write(path, bytes);
						
          	 //回显路径
            redirectAttributes.addFlashAttribute("message",
                    "You successfully uploaded '" + UPLOADED_FOLDER + file.getOriginalFilename() + "'");

        } catch (IOException e) {
            redirectAttributes.addFlashAttribute("message", "upload failed");
            e.printStackTrace();
            return "redirect:/file/status";
        }

        return "redirect:/file/status";
    }

审计函数

java中文件操作的函数特别多,有的是原始的字节字符流

java都是基于流的,还有好多都是后面有封装的,感觉如果不熟就直接 搜索file吧,再检查 过滤条件

  1. JDK原始的java.io.FileInputStream

  2. JDK原始的 BufferedOutputStream

  3. JDK原始的各种OutputStream,流操作都可以

  4. Apache Commons IO提供的org.apache.commons.io.FileUtils类

参考园长文章

修复方案

  • 使用白名单校验上传文件类型、大小限制、MIME类型
  • 白名单fileName.substring(fileName.lastIndexOf(".")); 检查后缀名
  • 还有一个BufferedImage类、Image类、Graphics类这些封装好的图片类,直接传进去试试
BufferedImage bi = ImageIO.read(file);

0x02文件读取

简述:

Java其实读写是一体的,都是流的输入和输出

这个漏洞主要是要结合第一个,路径穿越的情况

代码:

@GetMapping("/path_traversal/")
public String getImage(String filepath) throws IOException {
File f = new File(filepath);
if (f.exists() && !f.isDirectory()) {
		//读取文件
    byte[] data = Files.readAllBytes(Paths.get(filepath));
    return new String(Base64.encodeBase64(data));
} else {
    return "File doesn't exist or is not a file.";
}

审计函数

  1. JDK原始的java.io.RandomAccessFile类

  2. JDK原始的inputsteam类

  3. Apache Commons IO提供的org.apache.commons.io.FileUtils类

  4. JDK1.7新增的基于NIO非阻塞异步读取文件的java.nio.channels.AsynchronousFileChannel

  5. JDK1.7新增的基于NIO读取文件的java.nio.file.Files

    常用方法如:Files.readAllBytesFiles.readAllLines

    参考园长文章

修复方案:

过滤目录穿越关键字

0x01 目录遍历

简述:

目录遍历,主要看逻辑吧,能不能回显

有专门file.listFiles()函数可以处理。

代码:

package file;

import java.io.File;
import java.io.FileFilter;

public class filepath {
    public static void main(String[] args) {
        String path = "/Users/zy/Desktop/java_rmi/src/main/java/";		//要遍历的路径
        File file = new File(path);		//获取其file对象
        func(file);
    }

    private static void func(File file){
        File[] fs = file.listFiles();
        for(File f:fs){
            if(f.isDirectory())	//若是目录,则递归打印该目录下的文件
                func(f);
            if(f.isFile())		//若是文件,直接打印
                System.out.println(f);
        }
    }
}

你可能感兴趣的:(代码审计)