Skip to content

任意文件上传

en0th edited this page Jul 30, 2024 · 3 revisions

简介

任意文件上传漏洞是一种Web应用程序安全漏洞,攻击者可以利用此漏洞将任意文件上传到服务器上,从而实现攻击目的。攻击者通常可以上传包含恶意代码的Web Shell、病毒、木马程序等恶意文件,通过这些文件进行远程控制、信息窃取、篡改网站内容、网站挂马等攻击行为。 任意文件上传漏洞通常是由于Web应用程序的开发人员没有对上传的文件类型和文件大小进行充分的验证和过滤所致。攻击者可以通过修改上传的文件类型、伪造上传的文件头等方式绕过验证,上传任意类型和大小的文件。一旦攻击者上传了恶意文件,他们就可以在服务器上执行任意的命令,并获得系统权限,这将给Web应用程序带来严重的安全威胁。

攻略

1)前端校验文件后缀

考察:绕过前端校验方式

我们准备了一个JSP文件,名字叫做PrintTime.jsp

<%@ page session="false" %>Now time is: <%=new java.util.Date()%>

使用该文件上传提示我们文件不符合要求。

image-20240727155353714

如果正常上传png图片是可以的。

image-20240727155549545

此时我们直接修改数据包中的png为jsp发现可以上传成功。

image-20240727155643708

我们先进行手动劫持,然后正常在页面中点击上传图片。

然后在拦截到的修改数据包中的文件名和文件内容,点击提交数据。

image-20240727155902839

此时返回了JSP文件的访问地址,点击访问后成功打印了时间。

image-20240727160015591

image-20240727160023064

2)后端校验MIME TYPE

考察:绕过MIME TYPE校验

MIME TYPE其实就是上传文件数据包内的Content-Type。

直接上传JSP文件当然是不行的。

image-20240727160419713

我们只需要修改Content-Type为普通图片的类型可以了。

image-20240727160451174

3)后端文件后缀(黑名单)

考察:文件后缀黑名单校验

文件后缀的检测一般使用白名单,如果存在黑名单的情况就有可能会被绕过。

一般常见的思路是通过尝试上传多种可以被解析的后缀,但遗憾的是目前默认只解析jsp和jspx。

image-20240727162111638

任意修改后缀发现后缀a,能够上传成功,说明是黑名单拦截。

image-20240727163413570

如在Windows环境下运行,可以使用空文件名的方式,为文件名添加一个点PrintTime.jsp.,这样后端校验时因为没有获取到黑名单里的后缀名放行。

image-20240727165119150

此时我们再去访问/upload/PrintTime.jsp发现写入成功。

image-20240727165636875

其他情况可以使用大小写绕过,但需要注意的是,除了jsp和jspx,其他后缀文件都不会被解析成servlet。

image-20240727170122877

image-20240727170224761

4)上传压缩包

考察:制作路径穿越压缩包上传

在任意目录下创建文件夹test,在文件夹内创建PrintTime.jsp,内容为:

<%@ page session="false" %>Now time is: <%=new java.util.Date()%>

将该文件打包压缩成PrintTime.zip

image-20240729094528079

上传后返回了访问地址,但我们访问发现被禁止了。你也可以尝试路径校验绕过。

image-20240729095732621

根据之前发现,upload 目录是可以被访问的。我们希望文件名携带..\upload\。统计..\upload\一个为10个字符,我们将PrintTime替换为aaaaaaaaaaa共11个字符。

image-20240729111644692

使用notepad++打开aaaaaaaaaaa.zip

image-20240729111833677

将文件名的前10位修改成..\upload\,保存。

image-20240729111958380

重新上传压缩包,发现文件名已经改成我们想要的了。

image-20240729112128180

点击后能够正确访问,路径是upload,说明我们成功通过这种方式路径穿越上传了恶意文件。

image-20240729112255596

开发思路

通常我们上传文件会特别关注以下几个方面:

  1. 文件类型校验:根据文件后缀名或者文件头(magic number)判断文件类型,只接受安全的文件类型(白名单),如图片、PDF、文本等,拒绝危险的文件类型,如可执行文件等。
  2. 文件大小校验:限制文件大小,避免上传过大的文件。
  3. 文件名校验:防止上传包含危险字符的文件名,如 ../ 等。对文件进行重命名
  4. 文件内容校验:对于上传的文件,可以对其内容进行检查,如通过杀毒软件进行检查,避免上传带有病毒的文件。

除此之外还有其他防范的操作。

  • 文件下载时不提供文件名,只提供文件随机生成的ID。
  • 全站不解析JSP、JSPX等可以解析对象的文件。
  • 上传文件目录低权限、网站运行权限低权限等。

题外话,如果不校验文件内容,那么必须考虑文件包含漏洞存在的情况。

在开发时,我列举了前端检测后缀、后端检测MIME Type、后端检测后缀黑名单的情况。Tomcat10版本已经不使用ServletFileUpload而是使用request.getPart即可根据 name 获取文件。 代码来源:com/pika/electricrat/unsafeupload/dto/UploadServlet.java

// 后端检测MIME Type
@Api({RequestMethodType.POST})
public Map<?, ?> imageMIME(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    Part file = request.getPart("image_file");
    for(String i : FileServerImpl.IMAGE_FILE_TYPE){
        if (file.getContentType().equals("image/"+i)){
            return uploadFile(file, uploadPath(request));
        }
    }
    HashMap<String, Object> data= new HashMap<>();
    data.put("uploadStatus", false);
    return data;
}

// 后端检测后缀黑名单
@Api({RequestMethodType.POST})
public Map<?, ?> imageBlackList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    Part file = request.getPart("image_file");
    String fileName = file.getSubmittedFileName();
//        String suffixName = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
    String suffixName = fileName.substring(fileName.lastIndexOf("."));
    System.out.println(suffixName);
    for(String i : FileServerImpl.BLACK_FILE_TYPE) {
        if (suffixName.equals(i)){
            HashMap<String, Object> data= new HashMap<>();
            data.put("uploadStatus", false);
            return data;
        }
    }
    return uploadFile(file, uploadPath(request));
}

// 上传文件
private HashMap<String, Object> uploadFile(Part imageFile,String filePath){
    HashMap<String, Object> data= new HashMap<>();
    try {
        String fileName = imageFile.getSubmittedFileName();
        long fileSize = imageFile.getSize();
        if(fileSize > FileServerImpl.MAX_FILE_SIZE){data.put("uploadStatus", false);return data;}
        String fileType = imageFile.getContentType();

        File file = new File(filePath);
        if (!file.exists() && !file.isDirectory()){
            file.mkdir();
        }

        imageFile.write(filePath+"\\"+fileName);
        HashMap<String, Object> fileObject = fsi.uploadFile(new FileEntity(fileName, fileType, (filePath+"\\"+fileName),
                System.currentTimeMillis(), fileSize, (new ImageVerificationCode()).GetRandom(8)));
        if (fileObject.isEmpty()){
            data.put("uploadStatus", false);
            return data;
        }
        data.put("file", fileObject);
        data.put("uploadStatus", true);
    } catch (Exception e){
        data.put("uploadStatus", false);
        data.put("msg", e.getMessage());
    }
    return data;
}

后端检测MIME Type,可以抓包轻松修改。后端检测后缀黑名单出问题的是忽略了大小写。 String suffixName = fileName.substring(fileName.lastIndexOf(".")); 这句话本身是获取后缀,但对比时没有考虑到大小写。从下方的BLACK_FILE_TYPE可以看出,我们只需要修改后缀为.jSp就能绕过。 代码来源:com/pika/electricrat/unsafeupload/bo/Impl/FileServerImpl.java

public static final String[] IMAGE_FILE_TYPE = {"png", "jpg", "gif"};
public static final String[] BLACK_FILE_TYPE = {".html", ".htm", ".phtml", ".jsp", ".jspa", ".jspx", ".jsw", ".jsv", ".jspf", ".jtml"};

还有一种经典的文件上传后缀黑名单检测不严格造成的任意文件上传。它的代码和上面的很相似,只是将lastIndexOf换成了IndexOf String suffixName = fileName.substring(fileName.IndexOf(".")); 也就是说我们只需要将后缀改成.jpg.jsp即可绕过黑名单的检测。