Web安全之上传漏洞

0x00 任务

1、编写一个上传图片的功能页面

2、针对上传的文件进行验证(后缀验证、文件头验证、文件名验证等)

3、文件上传通常会与文件解析漏洞相结合,可以收集整理存在解析漏洞的组件和相关版本,无法部署相关环境,可以学习相关技术,不用实际操作

扩展学习:学习如何绕过黑名单验证、文件头验证,如何杜绝上传图片的功能无法利用获取 shell ?

0x01 客户端检测

(1)后缀检测(使用js检查不合法图片)

function checkFile() {
    var file = document.getElementsByName('upload_file')[0].value;
    if (file == null || file == "") {
        alert("请选择要上传的文件!");
        return false;
    }
    //定义允许上传的文件类型
    var allow_ext = ".jpg|.png|.gif";
    //提取上传文件的类型
    var ext_name = file.substring(file.lastIndexOf("."));
    //判断上传文件类型是否允许上传
    if (allow_ext.indexOf(ext_name + "|") == -1) {
        var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
        alert(errMsg);
        return false;
    }
}

函数解释

getElementsByName()

方法可返回带有指定名称的对象的集合。

document.getElementsbyname(name)[0].value
document.getElementsbyname(name)的意思就是获取当前页面上指定name名称的对象集合
页面上有可能不止一个叫name的对象,所以[0]就是获取第一个

Substring()

例如:

	string front = null;
	string back = null;
	string str = "abcdefg";
	front = str.Substring(0, 1);//从第一个开始截取,共截取一位
	back = str.Substring(str.Length - 1, 1);//从最后一个开始截取,共截取一位
	Response.Write(front + "***" + back);

输出效果:a***g

lastIndexOf()

例如:

string str = "abcdefg";
str = str.Substring(0, str.LastIndexOf("c"));
Response.Write(str);

输出效果:ab

就是截取c前面的字符串。

indexOf()

stringObject.indexOf(searchvalue,fromindex)

该方法将从头到尾地检索字符串 stringObject,看它是否含有子串 searchvalue。开始检索的位置在字符串的 fromindex 处或字符串的开头(没有指定 fromindex 时)。如果找到一个 searchvalue,则返回 searchvalue
的第一次出现的位置。stringObject 中的字符位置是从 0 开始的。indexOf() 方法对大小写敏感!如果要检索的字符串值没有出现,则该方法返回 -1。

绕过方式

可以利用burp抓包改包,先上传一个gif类型的木马,然后通过burp将其改为asp/php/jsp后缀名即可

0x02 服务端检测

(1)文件类型(MIME类型检测)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR))
    {
        if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))
        {
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) 
            {
                $img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
                $is_upload = true;
            }
        } 
        else $msg = '文件类型不正确,请重新上传!';
    } 
    else  $msg = $UPLOAD_ADDR.'文件夹不存在,请手工创建!';
}

函数解释

$_FILES['myFile']['type'] 文件的 MIME 类型,需要浏览器提供该信息的支持,例如"image/gif"。

move_uploaded_file() 函数将上传的文件移动到新位置。若成功,则返回 true,否则返回 false。
语法move_uploaded_file(file,newloc) 其中newloc是新的位置。

绕过方式

检测上传到服务器的文件类型,在 burp 抓包可以看到类似于 Content-Type: image/gif

使用 burp 抓包,上传本地文件 muma.php

在 burp 的 request 包里可以看到Content-Type: text/plain

修改:Content-Type: text/plain -> Content-Type: image/gif

像这种服务端检测http 包的Content-Type 都可以用这种类似的方法来绕过检测

(2)后缀检测(黑名单)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array('.asp','.aspx','.php','.jsp');
        $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)) 
        {
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR. '/' . $_FILES['upload_file']['name'])) 
            {
                 $img_path = $UPLOAD_ADDR .'/'. $_FILES['upload_file']['name'];
                 $is_upload = true;
            }
        }
        else $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
    } 
    else $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}

函数解释

(1)trim() 函数移除字符串两侧的空白字符或其他预定义字符。
trim(string,charlist)
string 必需。规定要检查的字符串。
charlist 可选。规定从字符串中删除哪些字符。如果被省略,则移除以下所有字符:
“\0” - NULL
“\t” - 制表符
“\n” - 换行
“\x0B” - 垂直制表符
“\r” - 回车
" " - 空格

(2)strrchr() 函数查找字符在指定字符串中从后面开始的第一次出现的位置,如果成功,则返回从该位置到字符串结尾的所有字符,如果失败,则返回 false。

(3)str_ireplace() 函数替换字符串中的一些字符(不区分大小写)
语法str_ireplace(find,replace,string,count)
参数 描述
find 必需。规定要查找的值。
replace 必需。规定替换 find 中的值的值。
string 必需。规定被搜索的字符串。
count 可选。一个变量,对替换数进行计数。

(4)in_array() 函数搜索数组中是否存在指定的值。
注释:如果 search 参数是字符串且 type 参数被设置为 TRUE,则搜索区分大小写。
语法in_array(search,array,type)

绕过方式

不允许上传.asp,.aspx,.php,.jsp后缀文件,但是可以上传其他任意后缀

.php .phtml .phps .php5 .pht

前提是apache的httpd.conf中有如下配置代码

AddType application/x-httpd-php .php .phtml .phps .php5 .pht

或者上传.htaccess文件:

(3).htaccess(没过滤.htaccess)

需要条件:

1.mod_rewrite模块开启

2.AllowOverride All

文件内容


  SetHandler application/x-httpd-php


此时上传shell.jpg文件即可被当作php来解析。
Web安全之上传漏洞_第1张图片

或者

AddType application/x-httpd-php .jpg

另外基本上所有的黑名单都可以用Apache解析漏洞绕过。

0x03 00截断

$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);//找到.的位置+1
    if(in_array($file_ext,$ext_arr))
    {
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['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类型文件!";
}

函数解释

strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。返回字符串在另一字符串中最后一次出现的位置,如果没有找到字符串则返回 FALSE

注释:strrpos() 函数对大小写敏感。

strrpos(string,find,start)

参数 描述
string 必需。规定被搜索的字符串。
find 必需。规定要查找的字符。
start 可选。规定在何处开始搜索。

substr() 函数返回字符串的一部分。

注释:如果 start 参数是负数且 length 小于或等于 start,则 length 为 0。

语法:substr(string,start,length)

绕过方式

CVE-2015-2348影响版本:

5.4.x<= 5.4.39, 5.5.x<= 5.5.23, 5.6.x <= 5.6.7 
exp:move_uploaded_file($_FILES['name']['tmp_name'],"/file.php\x00.jpg");

源码中move_uploaded_file中的save_path可控,因此00截断即可。
Web安全之上传漏洞_第2张图片
Web安全之上传漏洞_第3张图片

0x04 绕过总结

客户端绕过

可以利用burp抓包改包,先上传一个gif类型的木马,然后通过burp将其改为asp/php/jsp后缀名即可。

服务端绕过

文件类型绕过

我们可以通过抓包,将content-type字段改为image/gif

文件头绕过

在木马内容基础上再加了一些文件信息,有点像下面的结构

 GIF89a
 还有PE的MZ头,JPG的JFIF等等,GIF的GIF89a

文件后缀名绕过

前提:黑名单校验
黑名单检测:一般有个专门的 blacklist 文件,里面会包含常见的危险脚本文件。
绕过方法:
(1)找黑名单扩展名的漏网之鱼 - 比如 asa 和 cer 之类
(2)可能存在大小写绕过漏洞 - 比如 aSp 和 pHp 之类
能被解析的文件扩展名列表:

 jsp  jspx  jspf
 asp  asa  cer  aspx
 php  php  php3  php4
 exe  exee

配合文件包含漏洞

前提:校验规则只校验当文件后缀名为asp/php/jsp的文件内容是否为木马。
绕过方式:(这里拿php为例,此漏洞主要存在于PHP中)
(1)先上传一个内容为木马的txt后缀文件,因为后缀名的关系没有检验内容;
(2)然后再上传一个.php的文件,内容为



此时,这个php文件就会去引用txt文件的内容,从而绕过校验,下面列举包含的语法:

#PHP    



\#ASP    



\#JSP    



or

<%@include file="上传的txt文件路径"%>

配合服务器解析漏洞

详细可参考:http://thief.one/2016/09/21/服务器解析漏洞/

配合操作系统文件命令规则

(1)上传不符合windows文件命名规则的文件名

test.asp.
test.asp(空格)
test.php:1.jpg
test.php::$DATA
shell.php::$DATA…….

会被windows系统自动去掉不符合规则符号后面的内容。
(2)linux下后缀名大小写
在linux下,如果上传php不被解析,可以试试上传pHp后缀的文件名。

CMS、编辑器漏洞

(1)CMS漏洞:比如说JCMS等存在的漏洞,可以针对不同CMS存在的上传漏洞进行绕过。
(2)编辑器漏洞:比如FCK,ewebeditor等,可以针对编辑器的漏洞进行绕过。
这两方面的漏洞以后单独成文汇总,这里点到为止。

配合其他规则

(1)0x00截断:基于一个组合逻辑漏洞造成的,通常存在于构造上传文件路径的时候

test.php(0x00).jpg
test.php%00.jpg

路径/upload/1.php(0x00),文件名1.jpg,结合/upload/1.php(0x00)/1.jpg
伪代码演示:
name= getname(httprequest) //假如这时候获取到的文件名是 help.asp.jpg(asp 后面为 0x00)
type =gettype(name)        //而在 gettype()函数里处理方式是从后往前扫描扩展名,所以判断为 jpg
if(type == jpg)
SaveFileToPath(UploadPath.name, name)   //但在这里却是以 0x00 作为文件名截断
//最后以 help.asp 存入路径里

WAF绕过

垃圾数据

有些主机WAF软件为了不影响web服务器的性能,会对校验的用户数据设置大小上限,比如1M。此种情况可以构造一个大文件,前面1M的内容为垃圾内容,后面才是真正的木马内容,便可以绕过WAF对文件内容的校验;
Web安全之上传漏洞_第4张图片

0x05 参考文章

文件上传验证绕过技术总结

upload-labs

图片马二次渲染

upload超全

你可能感兴趣的:(web安全学习)