upload-labs Pass21

管理员 2020-02-10 PM 168℃ 0条

1.先查看源码

$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
    //检查MIME
    $allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
        $msg = "禁止上传该类型文件!";
    }else{
        //检查文件名
        $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
        if (!is_array($file)) {
            $file = explode('.', strtolower($file));
        }

        $ext = end($file);
        $allow_suffix = array('jpg','png','gif');
        if (!in_array($ext, $allow_suffix)) {
            $msg = "禁止上传该后缀文件!";
        }else{
            $file_name = reset($file) . '.' . $file[count($file) - 1];
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' .$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $msg = "文件上传成功!";
                $is_upload = true;
            } else {
                $msg = "文件上传失败!";
            }
        }
    }
}else{
    $msg = "请选择要上传的文件!";

2.下面我分析下主要的过滤流程

下面这句检查有没有输入保存名称,有就让其作为下一步过滤要操作的数据,没有就是使用上传的文件的文件名,假设我们这里自定义的文件名是 info.php.jpg

$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];

这句判断我们输入的数据是不是数组,是的话不进行操作,不是的话就以点作为分割符,分割数据成为数组,关于数组

if (!is_array($file)) {
$file = explode('.', strtolower($file));
}

因为我们输入的数据不是数组,所以会被转为数组,默认是这样:array([0]=>info [1]=>php [2]=>jpg),0 1 2表示的是他的 ID键,比如 $file[0] = "info",$file[1] = "php" ..........

end($file); 意思是取数组中的最后一个元素,也就是 "jpg" ,然后去与白名单对比,白名单里面有 jpg,所以我们通过了这层过滤。

$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
}

然后就进到了文件命名保存的环节,我们发现文件命名的规则是这样:

 $file_name = reset($file) . '.' . $file[count($file) - 1];

reset($file):取数组的第一个个元素,也就是 "info";count($file):统计数组中的元素数量,也就是 3 ;$file[count($file) - 1]:数组中元素数量减一的ID键的元素,那么就是 $file[3 - 1] 是$file[2],是 "jpg",最后保存的文件名是"info.jpg"

3. 如果是正常思维来讲,无论如何都是没有办法绕过的,但是有个地方给了一个提示。

$file[count($file)-1]也就是最后一个元素,但是为什么不直接使用end($file)呢?难道会存在两者不对等的情况?因为数组中的 ID键我们是可以自定义的,那么我们定义一个数组,$file = array([1]=>info [2]=>jpg),这种情况下 end($file)的值就是 "jpg",但是 $file[count($file)-1]也就是$file[1] 就成了info,那么最终文件的命名会成为 info.info。如果把ID键1的值改为 "php"呢?,嘿嘿

4.而前面出现了检查我们输入的是不是数组,不是就转为数组,是就不操作

那么我们可以从这点下手,我们直接向服务器传递数组看会怎么样

下面是原始的包,save_name的值是字符串"info.php.jpg"

下面是修改后的包,sava_name的值是数组,array([1]=>php [2]=>jpg)

可以执行

如果想要自定义文件名,可以这样修改数组ID键跟值达到目的。

这是为什么呢?这很好解释,文件名的命名是数组第一个元素 加点加ID键值为数组数量减一的元素,那就是 ($file[0] .$file[1])因为我们这里没有传递 $file[1],那就为空(null),所以文件名是 index.html. ,最后那点到保存到服务器时会被自动删除。

原理大概就是这样子,这一时间又是数组啊又是ID键的啊估计把大家搞懵了,大家好好消化下,做题最好的就是把过滤的流程给整明白了,这样我们才能知道为什么可以这样绕过,不懂的函数要积极的去网上查。

关于请求包为什么可以修改,这就要说到http协议里面的内容,这个大家感兴趣的可以去查阅相关资料。

5.这是文件上传最后一关了,如果你把这21关的过滤原理跟绕过给整明白了,说明你对文件上传已经有了一定的理解,不过学无止境,靶场永远是一个温室,实际的渗透测试中,我们遇到的困难会比这些复杂的多,希望同学们永远怀着一颗学徒的心,对技术的追求不敷衍、不懈怠、不自大、不放弃!

非特殊说明,本博所有文章均为博主原创。

评论啦~