信息安全文件上传漏洞upload-labs第21关详解

信息安全文件上传漏洞upload-labs第21关详解,第1张

正文部分分为两部分:1.过关流程,2.流程分析

本关涉及知识点:

1.一定的代码审计能力

2.数组验证

3.文件名/.上传绕过(例如a.php/.上传到目录后就变成了a.php,这是由于文件名无法识别且后缀无法识别,最终就向前识别,实现了绕过)

正文:

过关流程:

1.编写一句话木马(php,post方式,参数为x,我将其命名为oneword.php):

2.上传该木马并用burp抓包

 得到该数据包。

3.将Content-Type字段改为被允许的任意一种(此处我改为的是image/jpeg)并将数据包中的name="save_name"以数组的形式发送,并将数组的最后一位改成可被成功放行的(jpg,png,gif)中的任意一种。

'image/jpeg','image/png','image/gif'

4.放包,成功上传

 

思路分析:

 思考:这是第21关的样子,目的是上传一个webshell到服务器,可控的部分在我能看到的就是1.由自己选择上传的文件,2.文件保存到对方服务器的名称。浅看一下提示:

 这关来源于ctf比赛,还需要一定的代码审计能力。(由于版本问题,这里的21关显示的是20关,但其实这是21关)

查看代码:

$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 = "请选择要上传的文件!";
}

代码分析:

$allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
        $msg = "禁止上传该类型文件!";

该部分代码从MIME上进行了过滤,也就是数据包中的Content-Type字段,该部分可控,可以由我们自主修改,直接改成允许的就行了(image/jpeg,image/png,image/gif)。 

        $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
        if (!is_array($file)) {
            $file = explode('.', strtolower($file));
        }

 检查上传的文件在save_name那栏是否为空(也就是可控部分的保存名称那里),若为空,则以上传的文件名为准命名,不为空,则以save_name(也就是可填写的保存名称那里)为准进行命名,并把该名称传给$file参数,然后利用strtolower函数将$file小写,检查该参数是否为数组,若不是数组,则以'.'为准将其打成数组(例如oneword.php就打成file[0]='1',file[1]='.',file[2]='php')。

$ext = end($file);
        $allow_suffix = array('jpg','png','gif');
        if (!in_array($ext, $allow_suffix)) {
            $msg = "禁止上传该后缀文件!";

用$ext接收$file参数(数组)的最后一位,并检查该位是否是可上传的jpg,png,gif.

 $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 = "文件上传失败!";
            }

 最后文件名为reset($file)+'.'+$file[count($file)-1](这里我写+是因为在php语言中.就是连接符,),这句话的意思就是以file这个数组的第一位和最后一位(数组的计数是从array[0]开始)用.连接起来,(例如数组file[0]='oneword',file[1]='.',file[2]='php'经过这句话后就变成了oneword.php)然后将最终的文件名上传到UPLOAD_PATH目录下。这里的count函数很重要,或者说这个count至关重要

 整体的流程就是先接收文件名,进行MIME验证,然后将文件名以'.'为分界打散成数组,对数组的最后一位,也就是文件后缀进行验证,最后将通过验证的数组重新组装上传至目录。

所以我们要做的有两个事:

1.改MIME:

将Content-Type字段改了就行

2.以数组形式传参对数组验证进行绕过:

构造file[0]=oneword.php/,file[1]为空,file[2]=jpg(能通过验证的jpg,png,gif其中任意一种),这样检测数组最后一位的时候通过验证,最后组装的数组的时候组装成了oneword.php/.上传到目录后就变成了oneword.php。这里的问题关键就是为什么数组的最后一位jpg为什么没有被拼接到最终的文件名中,原因在于这个拼接过程用的是count函数,如果数组有三位,但是有一位为空,最后count出来的数就为2,而非为3。按照这个想法,如果file[1]为空,最后就是这个空被拼接到了最后的文件名的最后面。所以上述构造成立。

 

 此处修复建议:将最后拼接文件名的count函数直接改为end($file)

验证:

 

 同样的数据包发送:

修改有效

 

 如对此关还有任何问题,或者对upload-labs任何一关有疑问欢迎私信我

文章如有错误欢迎大佬指正

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/993090.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存