Pass-01

首先我们尝试上传php文件,他会提示可以上传的文件类型以及你的文件类型

我们发现他是前端验证的文件后缀名,所以我们可以直接上传结尾为.jpg的shell文件,然后抓包修改后缀。

修改文件后缀:

右击图片获取路径:

http://192.168.252.168/upload-labs-master/upload-labs-master/upload/1.php

成功:

分析源码:

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $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 = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

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

我们可以看到在php文件中没有对文件名过滤,这是判断文件路径是否存在,而过滤文件名放在了前端的js函数中,所以我们可以直接绕过过滤。

Pass-02

这一关中,我们还是先上传一个php文件,提示文件类型不正确,请重新上传,没有提示白名单的后缀。

我们继续测试,发现这一次是服务器验证文件类型,我们还是上传图片shell,修改文件名最后上传成功。

http://192.168.252.168/upload-labs-master/upload-labs-master/upload/Pass2.php


我们首先判断php和jpg上传的时候,两处明显的不同时文件类型和文件名,我们将文件名修改为php可以成功上传,说明后端只验证请求包中的文件类型,我们可以分析一下源码。

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

我们可以看到这次的上传判断中添加了请求包中的文件类型,如果文件类型是图片类型就可以成功上传。

Pass-03

这一关我们最开始还是测试php文件,看来是有黑名单。

我们这里尝试绕过黑名单,比如.phtml .phps .php5 .pht,但是执行这种文件有条件,需要我们apache中的httpd.conf配置有如下代码:

AddType application/x-httpd-php .php .phtml .phps .php5 .pht

这里我们没有配置这个参数,所以我们可以在虚拟机中的phpstudy中配置一下
首先:在phpstudy中点击其他选项->配置文件->httpd.conf,然后找到这一行,把注释删掉。

我们在这里上传.phtml类型的文件,尝试绕过其黑名单。

完成上传,我们尝试访问,成功。

http://192.168.252.130/upload/202007120956534008.phtml

Pass-04

我们首先还是尝试上传php文件,返回不允许上传此文件,我们接着将上传类型改为图片尝试,失败。

这一关我们尝试上传.htaccess文件,.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。
概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,我们可以让其将.jpg文件当做php文件来解析,所以我们可以尝试上这个文件,文件中的代码如下:

SetHandler application/x-httpd-php

成功上传:

接着我们直接上传一个后缀为jpg的shell文件,访问成功执行。

http://192.168.252.130/upload/1.jpg


我们本关测试完成之后记得把这个文件删除,因为这个文件会将这个文件夹下的所有文件当做php来执行,所以为了不影响以后的测试需要把其删除。
我们可以分析一下源码:

$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.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

我们看了源码之后发现,phtml已经被过滤掉了,而且有大小写转换,但是没有过滤.htaccess文件,我们依靠这个漏洞完成了上传。

Pass-05

这一关,我们还是首先尝试上传php文件失败,图片类型失败,pthml类型失败,.htaccess文件失败。
经过测试前几个关卡的,都已将失效我们可以分析一下源码。

$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 = 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 = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

我们看到源码之后首先看到黑名单中已经把.htaccess文件过滤掉了,但是我们发现这一关中后续处理过程出现了问题,没有将大小写统一转换,所以我们可以使用将后缀大小写转换来绕过,知道这一点之后我们就开始上传。

上传成功,尝试访问,成功:

http://192.168.252.130/upload/202007121018029172.Php

Pass-06

这一关中经过我们测试,之前的都已经失效,我们分析一下源码,查看这一次的处理方式有哪些变化。

$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 = $_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)) {
            $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 = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

我们可以看到,这次的大小写转换也已经添加,所以大小写的方法已经失效,但是我们发现,其去除空格的部分去掉了,我们可以尝试在后缀添加一个空格来绕过过滤。
开始上传,成功。

访问连接,成功执行。

http://192.168.252.130/upload/202007121026313857.php

Pass-07

经过前几关的套路以及我们的测试,之前可以使用的方式已经不能再使用了,我们还是先分析源码

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

这一关的套路是把之前多文件名中多余的点去除的那一项给删除了,导致没有去除多余的点,我们可以尝试在上传的时候在最后添加一个多余的点,这样它使用strrchr拿到最后一个点之后的内容就不是我们的后缀,而在windows系统中,会自动将文件名后缀最后多余的点删除,我们可以利用这一点。
开始上传:

成功上传,我们尝试访问链接,成功:

http://192.168.252.130/upload/1.php.

Pass-08

根据我们之前的测试,我们发现他是通过将之前的过滤点删除来达到目的的,我们以后的测试可以先尝试分析源码,通过分析源码来更好的解题。

$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 = 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 = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

这一关中,我们发现过滤中少了::$DATA,我们可以使用windows文件流的特性,这个必须结合php使用。php+windows的情况下如果文件名中包含::$DATA,会把::$DATA之后的数据当成文件流来处理,不会检测后缀且保持::$DATA之前的文件名。
我们就在后面添加::$DATA来尝试绕过

成功上传,我们尝试访问链接,成功执行php代码

http://192.168.252.130/upload/202007121131412162.php::$data


但是在访问的时候我们需要注意,将::$DATA去掉才能正确访问。

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

在这里我们首先发现文件名使用的是我们上传时候的原始文件名,而不是像之前随机生成文件名,然后加上我们的后缀,我们在根据其之前的过滤规则,我们可以找出入下绕过手法,首先其会过滤掉点,然后过滤过滤::$DATA和空格,所以我们可以构造出1.php. .
尝试上传:

尝试访问链接,成功:

http://192.168.252.130/upload/1.php.


在尝试这一关上传的时候,我使用了1.php.::DATA.的方式进行了上传,最终上传成功的文件名是1.php.,奇葩的是这个文件无法打开,无法删除,只要一删除就提示不存在:

所以建议不要使用这种方式测试上传(靶机win7)。

Pass-10

直接分析源码:

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

我们看到源码之后,我们发现这一次的过滤是直使用str_ireplace函数将黑名单中的后缀进行替换,但是我们发现了问题所在

str_ireplace() 函数替换字符串中的一些字符(这个不区分大小写,str_replace() 区分大小写)。
该函数必须遵循下列规则:
如果搜索的字符串是一个数组,那么它将返回一个数组。
如果搜索的字符串是一个数组,那么它将对数组中的每个元素进行查找和替换。
如果同时需要对数组进行查找和替换,并且需要执行替换的元素少于查找到的元素的数量,那么多余元素将用空字符串进行替换
如果是对一个数组进行查找,但只对一个字符串进行替换,那么替代字符串将对所有查找到的值起作用。

经过我们测试,它仅仅会对字符串从头到尾做一次审查,测试结果如下:

第一种有多个匹配结果,全部替换,第二个只有一个匹配结果,但是替换之后又生成一个匹配的结果,但是没有进行替换,我们可以使用这这种方式进行绕过。

成功上传,我们尝试访问链接,成功。

http://192.168.252.130/upload/1.php

最后修改:2020 年 08 月 26 日
如果觉得我的文章对你有用,请随意赞赏