upload-labs

Pass-01

进入第一关,直接就是一个上传功能点。

随手上传一个php文件:

<img src=’Upload-labs/1.png’)>

然后试试BP抓包发现抓不到包,那么就是前端校验了。

直接先上传一个yijuhua.jpg然后上传后抓包修改为php为再发包就上传成功了。

image-20200802162859389

image-20200802162936329

第一关就先把flag拿到手。用蚁剑连上去拿flag:

image-20200802163315403

最后分析一下源代码:

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;
    }
}

的确是前段校验,js判断的文件类型并弹框提示,这种直接先上传jpg抓包改后缀即可绕过。

Pass-02

老规矩php文件上传提示文件类型不正确,请重新上传:

抓包先:

该成后缀为.jpg后依然不正确:

Content-Type: application/octet-stream
猜测是Content-Type错误的原因。
换成:Content-Type: image/jpeg

发现即使是php后缀改为image/jpeg后依然成功上传。

证明后端代码只检查了其Content-Type来判断是否为图片因此被绕过。

代码:

$is_upload = false;
$msg = null;
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 = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

可以看到上面的判断方式的确是只判断了Content-type是否为image.jpeg这种导致上传直接修改类型绕过。

Pass-03

再次上传.jpg并改为php后返回包提示:

image-20200802164900580

很明显后端校验是黑名单,用一个phmtl就成功绕过了。

代码:

$is_upload = false;
$msg = null;
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, '.');
        $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)) {
            $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 = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

可以看到黑名单校验是极其不安全的,写漏一个都是有危害的,因而白名单就比较安全了。

Pass-04

多番尝试后发现都被ban掉了。最后发现.htaccess文件可以上传成功。

.htaccess:htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>
使用上面的htaccess文件上传后就可以把带有Jpg格式的文件全部解析为php文件了

image-20200802165900568

这样解析了后jpg也能当木马使。

代码:

$is_upload = false;
$msg = null;
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);//删除文件名末尾的点
        $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)) {
            $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 . '文件夹不存在,请手工创建!';
    }
}

可以看到黑名单很长一串,可惜永远可以绕过。

Pass-05

直接查看代码发现在pass-04上面加上了.htaccess。

这个ban了还有.usr.ini呀。

在usr.ini中写入:
auto_prepend_file=yijuhua.jpg

就能把yijuhua.jpg包含在php文件中。而php的文件包含的同时会默认将其当做php文件执行因而导致getshell成功。

Pass-06

同样是黑名单,然后大小写没过滤全依然大小写getshell.

Pass-07

上传yijuhua.php.即可。

原理:windows下默认不解析这样的文件因而会自动将后面的.删除掉导致最后上传成功。

至于为啥它是windows,能访问成功就默认了呗。

Pass-08

同7

Pass-09

$is_upload = false;
$msg = null;
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);//删除文件名末尾的点
        $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)) {
            $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_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

这个::$DATA是啥东西?

这里上传yijuhua.php::$data即可上传成功。
原理:php在window的时候如果文件名+"::DATA"会把::DATA"会把::DATA之后的数据当成文件流处理,不会检测后缀名.且保持"::$DATA"之前的文件名
因此这个也只能在windows环境下有效
但我依然不知道这是个啥。。。。

Pass-10

上传文件后发现php没了。试了试双写成功绕过:

image-20200802174744233

$is_upload = false;
$msg = null;
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 = 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)) {
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

关键代码:

$file_name = str_ireplace($deny_ext,"", $file_name);

可以看到直接采用替换函数将黑名单后缀替换为空,只替换过一次所以双写绕过。