Web安全:浅谈文件解析漏洞:黑名单绕过 (3-10)

一、黑名单绕过

看源码前,我们来补充一些基础知识:
1.in_array()函数:

in_array() 函数搜索数组中是否存在指定的值。

例如:

下面来看看黑名单防御的代码:

$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 . '文件夹不存在,请手工创建!';
    }
}

这个黑名单如何绕过呢?开发人员将后缀名提取出来,去掉了前后的空格,全部转化为小写,防止大小写绕过。其实,它的黑名单并不全面,看似层层防御,固若金汤,但是还是百密一疏。

在php被设计出来的时候,默认.php3 .php4 .php5 .phtml 等后缀名都会被当做.php来解析,既然它的黑名单里面没有这些。那么我们就可以上传以这些为后缀的文件。

二、.htaccess文件绕过

什么是.htaccess文件呢?掌控安全的网课可以说讲解的非常迷,经过我多方资料查找,通俗易懂的解释如下:

.htaccess文件是放置在服务器上的一个Apache配置文件,在这个文件中,包含有一条或多条配置指令,将它放置于某个目录下,这些配置指令对当前目录和其所有子目录生效。

用配置文件中指令,可以实现 用户验证等众多功能。

辣么,如何在macOS 上创建一个.htaccess 文件呢?注意我们要创建的是一个真正命名为 .htaccess的文件,而不是一个 文件名.htaccess文件。

首先打开文本编辑器,创建一个rtf文件,将这个文件保存到服务器相应的目录下,修改文件名为.htaccess,这个时候你会发现,无法修改,因为.htaccess作为一个隐藏文件已经被系统预留了。接下来,我们只需要在终端输入如下指令即可:

在mac上新建了一个.htaccess文件,居然提示.开头的文件会被隐藏,怎么显示呢?

打开终端,运行:

defaults write com.apple.finder AppleShowAllFiles TRUE 

意思是:设定隐藏文件为可见

然后重启Finder:

killall Finder 

恩,可以看见了吧,我们这个时候编辑.htaccess文件,编辑成功后,我们可以再次将它隐藏起来。

运行以下命令,然后再次重启Finder即可:

defaults write com.apple.finder AppleShowAllFiles -bool false  

OK,大功告成!

在windows下创建.htaccess文件更为简单,只需要先创建一个1.txt文件,然后修改文件名为.htaccess即可,如果提示必须输入文件名,可以采用命令行中的相关指令:ren 1.txt .htaccess 意思为将1.txt重命名为.htaccess,ren表示重命名。

因为最近换了MacBook,在此补充下Linux和mac下如何制作图片马:

首先在桌面上创建file1.txt并在里面写入一句话木马,下载file2.jpg,然后,打开终端,cd 到desktop

将file1.txt追加到file2.jpg后面的方法:
cat file1.txt >> file2.jpg
若要将两个文件合并成第三个文件,而不是追加,那么用命令:
cat file1.txt file2.jpg >> file3.jpg

第五关:后缀名大小写绕过

假如之前的黑名单验证的脚本代码中没有防止大小写,那么我们只要修改原本小写的后缀名为大写,直接上传.php文件就可以了。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
        $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)) {
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
                $img_path = $UPLOAD_ADDR . '/' . $file_name;
                $is_upload = true;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

第六关:文件后缀名空绕过

假如之前的黑名单脚本代码中没有trim函数,还去掉后缀名之后的空格的话,我们就可以通过在后缀名之后添加空格,来绕过黑名单。

注意,直接修改文件后缀名,添加空格是行不通的,必须通过抓包来修改后缀名。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
        $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)) {
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
                $img_path = $UPLOAD_ADDR . '/' . $file_name;
                $is_upload = true;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

剖析实现原理:我们通过抓包的修改后的文件名,会自动存储在超全局变量$_FILES中,所以在脚本核对$_FILES中文件的相关信息时,后缀名能够成功绕过黑名单,但是,当文件被上传到服务器中的时候,服务器会先自动取掉后缀中最后的空格,然后将它作为.php文件解析。in a word,$_FILES数组中的信息和上传的服务器中的文件的信息是不一致的。

第七关:文件名后缀点绕过

假如后端黑名单代码中没有使用 deldot()函数来先删除文件名后缀的点再进行比对的话,我们就可以通过在文件名后缀添加点来绕过黑名单,同样,必须通过抓包来修改,在系统中直接修改再上传是行不通的,因为点会被忽略掉。类似的,当文件上传到服务器上时,文件名后缀的点同样先被忽略掉了,然后作为.php文件进行解析。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
        $file_name = trim($_FILES['upload_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 . '/' . $file_name;
                $is_upload = true;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

第八关:::$DATA windows文件流绕过

在进入主题之前,让我们先来学学,如何将一个文件隐藏到另一个文件中吧

在命令行中输入:echo abcd>>a.txt 表示将abcd写入到a.txt中

echo abcd >>a.txt:b.txt 则表示将abcd写入到b.txt,b.txt隐藏在a.txt中,这时桌面上只会创建一个空的a.txt文件,通过资源管理器查看,它的大小只有0kb,但是实际上,a.txt中隐藏着b.txt, b.txt是实际占据硬盘大小的。

那么如何打开b.txt呢?在命令行输入:notepad a.txt:b.txt即可。

那如果在命令行输入:echo abcd>>a.txt::$DATA 会怎么样呢?

系统会忽略::$DATA 然后创建a.txt 文件,将abcd写入。可以理解为::$DATA表示寄宿文件为空。

那么当我上传文件到服务上的时候,如果通过抓包在文件后缀上添加上::$DATA,那么存储在$_FILES超全局变量中的文件后缀中就带有::$DATA,但是当文件上传到服务器的时候,我们自己添加的后缀会被windows操作系统自动忽略,岂不美哉?

不过记得在访问上传文件时,去掉::$DATA哦

第九关:构造文件后缀绕过

is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
        $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 . '/' . $file_name;
                $is_upload = true;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

从源码来看,这个黑名单机制似乎已经很完善了,但是如果我们通过抓包将 上传文件的后缀修改为 .php....呢?很悲哀,失败了?

那要是修改为.php. .呢?这个后缀经过一系列处理,$file_ext = .php. 正好可以绕过黑名单,而根据windows的文件特性,.php. .和.php效果是相同的。

第十关:文件后缀双写绕过

先让我们来看下源代码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

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

从源代码可以看到,凡是黑名单后缀,都会被转换为空。那么我们将后缀修改为.pphp5hp怎么样呢?很悲催,新文件的名称为.p5hp 。那么如果将后缀修改为.phtmlhp, 还是很悲催,新文件的名称为 .php成功,那如果修改后缀为.pphphp呢,新的文件名为.php

由此可以推测 str_ireplace($deny_ext,"", $file_name);这个函数是从前往后遍历数组$deny_ext 和$file_name的。

 

综上所述,黑名单机制是非常不安全的,因为需要防范的点太多,如果建网站,最好用白名单机制。

你可能感兴趣的:(CTF,Web安全)