【小白必学】文件上传的漏洞介绍及常见防御方法

01
文件上传漏洞原理

在文件上传的功能处,若服务端脚本语言未对上传的文件进行严格验证和过滤,导致恶意用户上传恶意的脚本文件时,就有可能获取执行服务端命令的能力,这就是文件上传漏洞。

02
文件上传漏洞触发点

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

03
文件上传漏洞的形成条件

文件能够通过前端和后端过滤和文件处理

文件内容不会改变,能够被正确存储

存储位置在Web容器控制范围内

攻击者有权限访问存储目录并有权执行文件

只要破坏了其中的任一条件即可防止文件上传漏洞。

04
文件上传漏洞常见防御方法

0.无防护

以下代码实现了一个简单的文件上传功能

index.html

11<form action="/upload" method="post" enctype="multipart/form-data"> 22  <input type="file" name="uploadfile" > 33  <input type="submit"> 44</form>

上传文件的后端处理,基于Spring Boot

1 1package com.example.fileuploadvul.Controller; 2 2 3 3import org.springframework.stereotype.Controller; 4 4import org.springframework.web.bind.annotation.PostMapping; 5 5import org.springframework.web.bind.annotation.RequestParam; 6 6import org.springframework.web.multipart.MultipartFile; 7 7 8 8import java.io.File; 9 9import java.io.IOException; 1010 1111@Controller 1212public class UploadFile { 1313    @PostMapping("/upload") 1414    public String uploadFile(@RequestParam("uploadfile")MultipartFile file){ 1515//获取文件名 1616        String filename = file.getOriginalFilename(); 1717        //文件保存路径 1818        String path="F:\tmp\"; 1919        File outfile = new File(path + filename); 2020        try { 2121            file.transferTo(outfile); 2222        }catch (IOException e){ 2323            e.printStackTrace(); 2424        } 2525        return "success"; 2626    } 2727}

上面的代码没有任何的防护功能,存在文件上传漏洞。用户可以随意的上传任何文件、木马。

1.前端进行js校验

增加攻击成本,该种方式容易被绕过。

javascript示例:

1 1<form action="/upload" method="post"  onsubmit="return judge()" enctype="multipart/form-data"> 2 2    <input type="file" name="uploadfile" id="checkfile" > 3 3    <input type="submit" value="提交"> 4 4    <p id="msg"></p> 5 5</form> 6 6<script type="text/javascript"> 7 7 8 8    function judge(){ 9 9        var file=document.getElementById("checkfile").value; 1010        if (file==null||file==""){ 1111            alert("请选择要上传的文件"); 1212            // location.reload(true); 1313            return false; 1414        } 1515        var isnext=false; 1616        //定义允许上传的文件类型 1717        var filetypes=[".jpg",".png"]; 1818        //提取上传文件的类型,其中这里需要注意用lastIndexOf而非indexOf,否则会被1.php.php绕过 1919        var fileend=file.substring(file.lastIndexOf(".")); 2020        //判断上传文件类型是否允许上传写法一 2121        for (var i=0;i<filetypes.length;i++){ 2222            if (filetypes[i]==fileend){ 2323                isnext=true; 2424                break; 2525            } 2626        } 2727        if (!isnext){ 2828            document.getElementById("msg").innerHTML="文件类型不允许"; 2929            // location.reload(true); 3030            return false; 3131        }else { 3232            return true; 3333        } 3434        //判断上传文件类型是否允许上传写法二 3535        if (fileend.indexOf(filetypes) == -1) { 3636           var errMsg = "该文件不允许上传"; 3737            alert(errMsg); 3838            return false; 3939        } 4040    } 4141</script>

绕过方法:

这里限制了只能上传.jpg .png文件,但是攻击者可以用burpsuite拦截修改包进行绕过。

图片

如图,上传一个内容为php的jpg文件,然后在此处将filename修改为1.php,即可绕过前端js验证。

图片

另外也可以利用插件禁用js后进行提交。

2.白名单/黑名单

2.1黑名单

2.1.1java示例:

1 1@Controller 2 2public class UploadFile { 3 3    @PostMapping("/upload") 4 4    public String uploadFile(@RequestParam("uploadfile")MultipartFile file, Model model){ 5 5        boolean flag=true; 6 6        String filename = file.getOriginalFilename(); 7 7        System.out.println(filename); 8 8        String suffix=filename.substring(filename.lastIndexOf(".")); 9 9        String[] blacklist={".jsp",".php",".exe",".dll","vxd","html"};//后缀名黑名单 1010        for (String s : blacklist) { 1111            if (suffix.equals(s)){ 1212                flag=false; 1313                break; 1414            } 1515        } 1616        if (flag){ 1717            String path="src\main\resources\static\upload"; 1818            File fileDir = new File(path); 1919            File outfile = new File(fileDir.getAbsolutePath()+File.separator + filename); 2020            try { 2121                file.transferTo(outfile); 2222                return "success"; 2323            }catch (IOException e){ 2424                e.printStackTrace(); 2525            } 2626        } 2727        else { 2828            model.addAttribute("msg","非法文件类型"); 2929        } 3030        return "index"; 3131    } 3232}

2.1.2php示例:

1 1$is_upload = false; 2 2$msg = null; 3 3if (isset($_POST['submit'])) { 4 4    if (file_exists(UPLOAD_PATH)) { 5 5        $deny_ext = array('.asp','.aspx','.php','.jsp'); 6 6        $file_name = trim($_FILES['upload_file']['name']); 7 7        $file_name = deldot($file_name);//删除文件名末尾的点 8 8        $file_ext = strrchr($file_name, '.'); 9 9        $file_ext = strtolower($file_ext); //转换为小写 1010        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA 1111        $file_ext = trim($file_ext); //收尾去空 1212 1313        if(!in_array($file_ext, $deny_ext)) { 1414            $temp_file = $_FILES['upload_file']['tmp_name']; 1515            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;             1616            if (move_uploaded_file($temp_file,$img_path)) { 1717                $is_upload = true; 1818            } else { 1919                $msg = '上传出错!'; 2020            } 2121        } else { 2222            $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!'; 2323        } 2424    } else { 2525        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; 2626    } 2727}

《黑客&网络安全入门&进阶学习资源包》分享 (qq.com)

2.1.3绕过方法:

图片

如图成功上传了phps文件。

黑名单容易出现大小写(pHp)、特殊可解析后缀(pht)、.htaccess、点、空格、::DATA、双写等绕过问题。

另外可能使用Intruder模块进行枚举后缀名,如使用字典

图片

图片

另外,“判断文件后缀名是否存在黑名单中的字符,将对应的字符串替换为空”这种方式也是不可取的。

String[] blacklist={"jsp","php","exe","dll","vxd","html"};//后缀名黑名单

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

这种方式可以通过双写 phphpp 进行绕过。

2.2白名单

2.2.1java示例:

1 1String fileSuffix = fileName.substring(fileName.lastIndexOf(".")); 2 2String[] white_suffix = {"gif","jpg","jpeg","png"}; 3 3Boolean fsFlag = false; 4 4for (String suffix:white_suffix){ 5 5    if (contentType.equalsIgnoreCase(fileSuffix)){ 6 6        fsFlag = true; 7 7        break; 8 8    } 9 9} 1010if (!fsFlag){ 1111    return "suffix not allow"; 1212}

2.2.2php示例:

1 1$is_upload = false; 2 2$msg = null; 3 3if(isset($_POST['submit'])){ 4 4    $ext_arr = array('jpg','png','gif'); 5 5    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1); 6 6    if(in_array($file_ext,$ext_arr)){ 7 7        $temp_file = $_FILES['upload_file']['tmp_name']; 8 8        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; 9 9 1010        if(move_uploaded_file($temp_file,$img_path)){ 1111            $is_upload = true; 1212        } else { 1313            $msg = '上传出错!'; 1414        } 1515    } else{ 1616        $msg = "只允许上传.jpg|.png|.gif类型文件!"; 1717    } 1818}

对后缀进行白名单限制不易被绕过。

因此建议使用白名单而非黑名单。

3.检查文件类型MIME Type

3.1java示例

1 1//1、MIME检测 2 2    String contentType = file.getContentType(); 3 3    String[] white_type = {"image/gif","image/jpeg","image/jpg","image/png"}; 4 4    Boolean ctFlag = false; 5 5    for (String suffix:white_type){ 6 6        if (contentType.equalsIgnoreCase(suffix)){ 7 7            ctFlag = true; 8 8            break; 9 9        } 1010    } 1111    if (!ctFlag){ 1212        return "content-type not allow"; 1313    }

3.2php示例

11if (($_FILES['upload_file']['type'] == 'image/jpeg') ||  22($_FILES['upload_file']['type'] == 'image/png') ||  33($_FILES['upload_file']['type'] == 'image/gif'))

增加攻击成本,该种方式容易被绕过。

3.3绕过方法:

如图可以直接修改包。

图片

4.使用随机数改写文件名

4.1php示例

11if(in_array($file_ext,$ext_arr)){ 22     $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext; 33     rename($upload_file, $img_path); 44     $is_upload = true;

4.2java示例

11String uuid = UUID.randomUUID().toString(); 22fileName = uuid+fileName.substring(fileName.lastIndexOf("."));

能够增加攻击成本,另外可以防范某些特殊名称文件(shell.php.rar.rar)

5.对文件内容进行校验

5.1对文件头进行校验

11.jpg    FF D8 FF E0 00 10 4A 46 49 46 22.gif    47 49 46 38 39 61 33.png    89 50 4E 47

以图片为例,可以截取文件的头部几个字节进行校验,如使用getimagesize(不建议使用)

增加攻击成本,易被绕过,攻击者同样可以在文件的头部增加如下字节:

图片

5.2对文件内容进行检测

绕过<?:

1<script language='php'>@eval($_POST[cmd]);</script>

绕过php:

1<?= @eval($_POST['cmd']);?>

同时绕过:

1<script language='pHp'>@eval($_POST[cmd]);</script>

6.对文件的处理逻辑。

若先将文件放入路径,再去检验文件合法性并删除,会存在条件竞争漏洞。

6.1示例:

1 1if(isset($_POST['submit'])){ 2 2    $ext_arr = array('jpg','png','gif'); 3 3    $file_name = $_FILES['upload_file']['name']; 4 4    $temp_file = $_FILES['upload_file']['tmp_name']; 5 5    $file_ext = substr($file_name,strrpos($file_name,".")+1); 6 6    $upload_file = UPLOAD_PATH . '/' . $file_name; 7 7 8 8    if(move_uploaded_file($temp_file, $upload_file)){ 9 9        if(in_array($file_ext,$ext_arr)){ 1010             $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext; 1111             rename($upload_file, $img_path); 1212             $is_upload = true; 1313        }else{ 1414            $msg = "只允许上传.jpg|.png|.gif类型文件!"; 1515            unlink($upload_file); 1616       } 1717    }else{ 1818        $msg = '上传出错!'; 1919    } 2020}

先用move_uploaded_file 将上传的文件移动到上传目录,再判断后缀名并就删除文件。

我们多线程异步上传文件A并不断地访问自己上传的文件A,可以文件A进行删除之前,成功执行文件A,

若文件A代码为:

1');?>

则执行成功可以生成稳定的shell.php

解决方案:经过充分完整的检查之后再上传。

7.禁止上传文件被执行

7.1存储服务器

将存储上传文件的位置设计在另一台只具备存储功能的文件服务器或数据库上,与Web应用服务器分开,这样即使木马被上传进来,也因为文件服务器不能执行脚本而没有办法实施攻击。

7.2设置权限

改存储上传文件的目录的执行脚本的权限。如linux系统下使用chmod命令修改目录的rwx权限。

7.3修改配置

修改.htaccess、apache的httpd.conf配置文件、Nginx增加配置

7.3.1 .htaccess

11<FilesMatch ".(?i:php|php3|php4|php5)"> 22Order  allow,deny 33Deny  from  all 44</FilesMatch>

7.3.2 修改apache的配置文件httpd.conf

11<Directory D:\wwwroot\public\uploads> 22<FilesMatch ".(?i:php|php3|php4|php5)"> 33    Order  allow,deny 44    Deny  from  all 55</FilesMatch> 66</Directory>

7.4 隐藏上传文件路径

示例:使用blob把网站上的全部图片链接加密。

后台返回图片:

1 1protected void Page_Load(object sender, EventArgs e) 2 2    { 3 3        string url = Server.MapPath("~/Images/abg.jpg"); 4 4        Bitmap b = new Bitmap(url); 5 5        MemoryStream ms = new MemoryStream(); 6 6        b.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); 7 7        Response.ClearContent(); 8 8        Response.ContentType = "image/Jpg"; 9 9        Response.BinaryWrite(ms.ToArray()); 1010    }

JavaScript代码:

1 1    <img id="img1" src=""> 2 2    <script type="text/javascript"> 3 3        //创建XMLHttpRequest对象 4 4        var xhr = new XMLHttpRequest(); 5 5        //配置请求方式、请求地址以及是否同步 6 6        xhr.open('POST', '/Default2.aspx', true); 7 7        //设置请求结果类型为blob 8 8        xhr.responseType = 'blob'; 9 9        //请求成功回调函数 1010        xhr.onload = function(e) { 1111            if (this.status == 200) {//请求成功 1212                //获取blob对象 1313                var blob = this.response; 1414                //获取blob对象地址,并把值赋给容器 1515                $("#img1").attr("src", URL.createObjectURL(blob)); 1616            } 1717        }; 1818        xhr.send(); 1919    </script>

回到网页查看img标签src的地址:

图片

8.文件二次渲染

1 1$is_upload = false; 2 2$msg = null; 3 3if (isset($_POST['submit'])){ 4 4    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径 5 5    $filename = $_FILES['upload_file']['name']; 6 6    $filetype = $_FILES['upload_file']['type']; 7 7    $tmpname = $_FILES['upload_file']['tmp_name']; 8 8 9 9    $target_path=UPLOAD_PATH.basename($filename); 1010 1111    // 获得上传文件的扩展名 1212    $fileext= substr(strrchr($filename,"."),1); 1313 1414    //判断文件后缀与类型,合法才进行上传操作 1515    if(($fileext == "jpg") && ($filetype=="image/jpeg")){ 1616        if(move_uploaded_file($tmpname,$target_path)) 1717        { 1818            //使用上传的图片生成新的图片 1919            $im = imagecreatefromjpeg($target_path); 2020 2121            if($im == false){ 2222                $msg = "该文件不是jpg格式的图片!"; 2323                @unlink($target_path); 2424            }else{ 2525                //给新图片指定文件名 2626                srand(time()); 2727                $newfilename = strval(rand()).".jpg"; 2828                $newimagepath = UPLOAD_PATH.$newfilename; 2929                imagejpeg($im,$newimagepath); 3030                //显示二次渲染后的图片(使用用户上传图片生成的新图片) 3131                $img_path = UPLOAD_PATH.$newfilename; 3232                @unlink($target_path); 3333                $is_upload = true; 3434            } 3535        } else { 3636            $msg = "上传出错!"; 3737        } 3838 3939    }else if(($fileext == "png") && ($filetype=="image/png")){ 4040        if(move_uploaded_file($tmpname,$target_path)) 4141        { 4242            //使用上传的图片生成新的图片 4343            $im = imagecreatefrompng($target_path); 4444 4545            if($im == false){ 4646                $msg = "该文件不是png格式的图片!"; 4747                @unlink($target_path); 4848            }else{ 4949                 //给新图片指定文件名 5050                srand(time()); 5151                $newfilename = strval(rand()).".png"; 5252                $newimagepath = UPLOAD_PATH.$newfilename; 5353                imagepng($im,$newimagepath); 5454                //显示二次渲染后的图片(使用用户上传图片生成的新图片) 5555                $img_path = UPLOAD_PATH.$newfilename; 5656                @unlink($target_path); 5757                $is_upload = true;                5858            } 5959        } else { 6060            $msg = "上传出错!"; 6161        } 6262 6363    }else if(($fileext == "gif") && ($filetype=="image/gif")){ 6464        if(move_uploaded_file($tmpname,$target_path)) 6565        { 6666            //使用上传的图片生成新的图片 6767            $im = imagecreatefromgif($target_path); 6868            if($im == false){ 6969                $msg = "该文件不是gif格式的图片!"; 7070                @unlink($target_path); 7171            }else{ 7272                //给新图片指定文件名 7373                srand(time()); 7474                $newfilename = strval(rand()).".gif"; 7575               $newimagepath = UPLOAD_PATH.$newfilename; 7676                imagegif($im,$newimagepath); 7777                //显示二次渲染后的图片(使用用户上传图片生成的新图片) 7878                $img_path = UPLOAD_PATH.$newfilename; 7979                @unlink($target_path); 8080                $is_upload = true; 8181            } 8282        } else { 8383            $msg = "上传出错!"; 8484        } 8585    }else{ 8686        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!"; 8787    } 8888}

绕过方法:

对比上传前后的两个文件,找到没有变动的区域,插入php代码。

《黑客&网络安全入门&进阶学习资源包》分享 (qq.com)

参考文章:

1 1https://wiki.wgpsec.org/knowledge/ctf/uploadfile.html 2 2 3 3https://blog.csdn.net/cldimd/article/details/104992488 4 4 5 5https://github.com/c0ny1/upload-labs 6 6 7 7https://juejin.cn/post/6989580413333159949 8 8 9 9https://blog.csdn.net/liguangyao213/article/details/123430199 1010 1111https://blog.csdn.net/qq_43277152/article/details/113373721 1212 1313https://blog.csdn.net/qq_45570082/article/details/108910162 1414 1515https://xz.aliyun.com/t/3941 1616 1717https://blog.csdn.net/weixin_64551911/article/details/124627363?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-124627363-blog-105068212.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-124627363-blog-105068212.pc_relevant_recovery_v2&utm_relevant_index=5 1818 1919等。
点赞
收藏

评论区

加载中...

相关推荐

皕杰报表(关于如何上传和下载文件到数据库)

在皕杰报表中文件是否可以上传到数据库中,当然是可以的。然后在附件上传和下载中,设置相对路径或绝对路径,文件名称,文件类型和上传的空值条件(上传的大小,默认限制是5120kb和满足什么条件时上传)。在下载中选择相对路径或绝对路径,填写下载链接名称和下载文件名称。填报操作时有三个函数:filedata、filename、filepath。filedata:获取文

.net web core 如何编码实现文件上传功能

关于我前言在进行Web前后端分析开始时,我们经常会碰到文件上传的需求。上传用户头像,上传认证材料、审核材料等,这些都可以归类为文件上传功能。今天主要把自己在开发过程中的心得进行一个整理,供大家学习。开启静态文件中间件默认情况下,静态文件(如HTML、CSS、图像和JavaScript)是ASP.NETCore应用直接提供给客户端的资产。开启静态

SpringBoot2.0 基础案例(14):基于Yml配置方式,实现文件上传逻辑

本文源码GitHub地址:知了一笑https://github.com/cicadasmile/springbootbase一、文件上传文件上传是项目开发中一个很常用的功能,常见的如头像上传,各类文档数据上传等。SpringBoot使用MultiPartFile接收来自表单的file文件,然后执

Play 2.0 用户指南 - 文件上传 -- 针对Scala开发者

   处理文件上传   在form中指定multipart/formdata属性上传文件   上传文件的标准方式是指定form的一个特殊属性multipart/formdata,可以让你混合表单数据和表单文件附件。   开始编写HTML表单:@form(actionrou

SpringBoot2.0 基础案例(14):基于Yml配置方式,实现文件上传逻辑

本文源码GitHub地址:知了一笑https://github.com/cicadasmile/springbootbase一、文件上传文件上传是项目开发中一个很常用的功能,常见的如头像上传,各类文档数据上传等。SpringBoot使用MultiPartFile接收来自表单的file文件,然后执

FastDFS提供的API简介

 FastDFS提供的API及简要说明如下:  upload:上传普通文件,包括主文件  upload\_appender:上传appender类型文件,后续可以对其进行append、modify和truncate操作  upload\_slave:上传从文件  download:下载文件  delete:删除文件