资讯专栏INFORMATION COLUMN

upload-labs 文件上传靶机闯关记录

宋华 / 2807人阅读

摘要:由于上传后文件后缀仍为,服务器仍将该上传文件以解析。文件是服务器中的一个配置文件,它负责相关目录下的网页配置。文件流实际应用文件系统包括对备用数据流的支持。在中,此默认数据流称为。

靶机搭建
推荐使用Windows系统搭建,我最先使用docker pull c0ny1/upload-labs的镜像在Pass-03遇到问题,初步判断是该镜像内的php.conf文件配置错误导致。

下载地址(https://github.com/c0ny1/uplo...)

Windows系统下解压,第一次运行或者每次改变靶机环境的目录时,都要运行一下modify_path.bat文件之后,再运行phpStudy.exe启动环境。

靶机包含漏洞类型分类

判断上传漏洞类型的方法

闯关记录 Pass-01 提示与查看源码
本pass在客户端使用js对不合法图片进行检查!
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;
    }
}
解题思路

前端校验上传文件的后缀名是否为.jpg|.png|.gif,使用Burp Suite抓包拦截数据包并修改上传文件的后缀名即可绕过前端的后缀名限制。

解题步骤

准备上传文件,文件内容为

将文件命名为info.jpg

浏览器设置代理使得Burp Suite可以抓包,Burp Suite中的Proxy--intercept--intercept is on,抓取数据包为:


修改数据包为info.jpginfo.php后放行;

访问上传文件的url后显示:

Pass-02 提示与查看源码
本pass在服务端对数据包的MIME进行检查!
$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."文件夹不存在,请手工创建!";
    }
}
解题思路

对文件MIME类型进行了验证判断,即请求数据中的Content-Typeimage/jpeg|image/png|image/gif,在拦截请求包时修改该内容即可绕过上传限制。由于上传后文件后缀仍为php,服务器仍将该上传文件以php解析。

解题步骤

上传文件名为info2.php

Burp Suite抓包修改包内容为:

访问上传文件的url后显示:

Pass-03 提示与查看源码
本pass禁止上传.asp|.aspx|.php|.jsp后缀文件!
$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 . "文件夹不存在,请手工创建!";
    }
}
解题思路

黑名单判断,服务器端禁止上传".asp",".aspx",".php",".jsp"后缀的脚本文件,可以上传例如php3, phtml后缀的文件绕过,前提是配置文件(C:UsersxxxxxDesktopupload-labs-envApacheconfhttpd.conf)中有如下配置:

AddType application/x-httpd-php .php .php3 .phtml

服务器会将php3, phtml后缀的文件当成php解析。

解题步骤

修改上传文件的文件名为info3.php3

上传后访问可见phpinfo()

Pass-04 提示与查看源码
本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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后缀文件!
$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 . "文件夹不存在,请手工创建!";
    }
}
解题思路

依然是黑名单判断,不过这次的限制更多,但是没有包括.htaccess,可以利用配合Apache的.htaccess文件上传解析漏洞。

.htaccess

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

靶机中httpd.conf相关配置:

# AllowOverride controls what directives may be placed in .htaccess files.
# It can be "All", "None", or any combination of the keywords:
#   Options FileInfo AuthConfig Limit
#
AllowOverride All

造成了我们可以上传.htaccess文件解析漏洞,来绕过验证进行上传WEBShell。

解题步骤

准备1.htaccess:

AddType application/x-httpd-php .jpg

使得服务器将所有.jpg后缀的文件当做php文件解析

上传.htaccess:

Burp Suite抓包修改包内容为:


修改文件名为.htaccess并放行数据包

上传文件info4.jpg:

查看文件:

Pass-05 提示与查看源码
本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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|.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,但是源代码中没有$file_ext = strtolower($file_ext); //转换为小写这一行代码,可以使用大小写绕过。

解题步骤

上传文件名info5.PhP

Pass-06 提示与查看源码
本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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后缀文件!
$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 . "文件夹不存在,请手工创建!";
    }
}
解题思路

源代码缺少$file_ext = trim($file_ext); //收尾去空,可以文件名后缀增加空格绕过

解题步骤

上传文件名info6.php

Pass-07 提示与查看源码
本pass禁止上传所有可以解析的后缀!
$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 . "文件夹不存在,请手工创建!";
    }
}
解题思路

源代码缺少$file_name = deldot($file_name);//删除文件名末尾的点限制,可以文件名后缀增加.绕过,windows系统下会自动去掉后缀名中最后的.

解题步骤

上传文件名info7.php.

Pass-08 提示与查看源码
本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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|.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 = 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 . "文件夹不存在,请手工创建!";
    }
}
解题思路

源代码缺少 $file_ext = str_ireplace("::$DATA", "", $file_ext);//去除字符串::$DATA限制,可以文件名后缀增加::$DATA绕过

Windows下NTFS文件系统的一个特性,即NTFS文件系统的存储数据流的一个属性 DATA 时,就是请求 a.asp 本身的数据,如果a.asp 还包含了其他的数据流,比如 a.asp:lake2.asp,请求 a.asp:lake2.asp::$DATA,则是请求a.asp中的流数据lake2.asp的流数据内容。

NTFS文件流实际应用

NTFS文件系统包括对备用数据流的支持。这不是众所周知的功能,主要包括提供与Macintosh文件系统中的文件的兼容性。备用数据流允许文件包含多个数据流。每个文件至少有一个数据流。在Windows中,此默认数据流称为:$DATA

解题步骤

上传文件名info8.php::$DATA,访问时去除后缀

Pass-09 提示与查看源码
本pass只允许上传.jpg|.png|.gif后缀的文件!
$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 . "文件夹不存在,请手工创建!";
    }
}
解题思路

$img_path = UPLOAD_PATH."/".$file_name;得上传后文件的URL命名规则是由$file_name直接拼接而成

解题步骤

上传文件名info9.php. .

Pass-10 提示与查看源码
本pass会从文件名中去除.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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|.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","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);只对文件后缀名进行一次过滤,双写文件名绕过。

解题步骤

上传文件名改成info10.pphphp

Pass-11 提示与查看源码
本pass上传路径可控!
$is_upload = false;
$msg = null;
if(isset($_POST["submit"])){
    $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)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}
解题思路

服务器端上传文件名的后缀做了限制

$img_path = $_GET["save_path"]."/".rand(10, 99).date("YmdHis").".".$file_ext;可知上传路径命名规则使用用户get请求的save_path值拼接而成。

考虑使用上传路径名%00截断绕过,不过这需要对文件有足够的权限,比如说创建文件夹,上传的文件名写成1.jpg, save_path改成../upload/1.php%00,最后保存下来的文件就是1.php

解题步骤

修改save_path值为../upload/info11.php%00上传图片文件:

访问http://192.168.30.212//upload/info11.php

Pass-12 提示与查看源码
本pass上传路径可控!
$is_upload = false;
$msg = null;
if(isset($_POST["submit"])){
    $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)){
            $is_upload = true;
        } else {
            $msg = "上传失败";
        }
    } else {
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}
解题思路

$img_path = $_POST["save_path"]."/".rand(10, 99).date("YmdHis").".".$file_ext;Pass-11相比将GET换为了POST,思路相同

这次的save_path是通过post传进来的,在进行00截断时需要在hex中修改

解题步骤

修改post参数的值,这里在php的后面添加了一个空格和字母a,其实写什么都可以,只是一般空格的16进制为0x20,比较好记,加个a好找到空格的位置,如果写个任意字符,再去查他的16进制表示也可以:

打开hex,修改16进制内容:

修改前

修改后

访问上传文件:

路径:

访问http://192.168.30.212/upload/info12.php

00截断原理 原理

系统在对文件名按16进制读取文件(或者说二进制)时,如果遇到0x00(ascii码为零),就会认为读取已结束。所以本来上传的info12.jpg文件名就被替换为info12.php

%000x00截断

原理一样,只是在Pass-11中为GET方式,服务器在进行URL解码时将其解码成0x00,Pass-12中为POST方式,没有URL解码这一步骤,所以要在hex值中修改,形成0x00截断。

Pass-13 提示与查看源码
本pass检查图标内容开头2个字节!
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 = "上传出错!";
        }
    }
}
解题思路

服务端检测文件头,并将上传文件的后缀重命名为检测到的文件类型

关于服务端检测文件头,我们可以在文件起始加入jpg|png|gif文件的文件头来绕过

十进制转十六进制: gif为47 49 , png为89 50, jpg为ff d8

关于文件上传后被重命名为图片文件,不能当做php解析,我们可以利用文件包含漏洞

解题步骤 gif

gif文件头为GIF89a ,上传文件info13.php:

GIF89a

利用文件包含漏洞访问上传文件,http://10.0.0.108/include.php?file=./upload/8820190612142038.gif

png

文件头标识 (8 bytes) 89 50 4e 47 0d 0a 1a 0a

利用文件包含漏洞访问上传文件

jpg

文件头标识 (2 bytes): ff, d8

利用文件包含漏洞访问上传文件

Pass-14 提示与查看源码
本pass使用getimagesize()检查是否为图片文件!
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()检查是否为图片文件:

getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
示例:

返回结果:

Array
(
    [0] => 350
    [1] => 318
    [2] => 2
    [3] => width="350" height="318"
    [bits] => 8
    [channels] => 3
    [mime] => image/jpeg
)

我们用到了索引[2],给出的是图像的类型,返回的是数字,其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM

与上一题解题思路一致,修改文件头绕过,但是本题的验证不只是文件头的前两位。

解题步骤 gif

文件头:GIF89a

png

文件头hex:89 50 4e 47 0d 0a 1a 0a

jpg

上传正常jpg图片,抓包修改包内容,文件末尾加入

利用文件包含漏洞访问上传文件:

初次尝试出错:

猜测可能是文件内容有干扰PHP解析的数据出现,后删除部分内容上传后成功

Pass-16 提示与查看源码
本pass使用exif_imagetype()检查是否为图片文件!
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()函数是php内置函数,用来获取图片类型,思路同上题,文件头绕过

解题步骤

Pass-16

Pass-16 提示与查看源码
本pass重新渲染了图片!
$is_upload = false;
$msg = null;
if (isset($_POST["submit"])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES["upload_file"]["name"];
    $filetype = $_FILES["upload_file"]["type"];
    $tmpname = $_FILES["upload_file"]["tmp_name"];

    $target_path=UPLOAD_PATH."/".basename($filename);

    // 获得上传文件的扩展名
    $fileext= substr(strrchr($filename,"."),1);

    //判断文件后缀与类型,合法才进行上传操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromjpeg($target_path);

            if($im == false){
                $msg = "该文件不是jpg格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".jpg";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH."/".$newfilename;
                imagejpeg($im,$img_path);
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "png") && ($filetype=="image/png")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefrompng($target_path);

            if($im == false){
                $msg = "该文件不是png格式的图片!";
                @unlink($target_path);
            }else{
                 //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".png";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH."/".$newfilename;
                imagepng($im,$img_path);

                @unlink($target_path);
                $is_upload = true;               
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "gif") && ($filetype=="image/gif")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromgif($target_path);
            if($im == false){
                $msg = "该文件不是gif格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".gif";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH."/".$newfilename;
                imagegif($im,$img_path);

                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }
    }else{
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}
解题思路

程序通过imagecreatefromjpeg()函数调用了PHP GD库(GD库,是php处理图形的扩展库),对图片进行了转换。

将一个正常显示的图片,上传到服务器。下载被渲染后与原始图片对比,在仍然相同的数据块部分内部插入Webshell代码,然后上传。

特殊的上传技巧,绕过PHP图片转换实现远程代码执行

大佬解题:https://xz.aliyun.com/t/2657

解题步骤

http://www.secgeek.net/POC/PO...
使用链接中的图片上传

Pass-17 提示与查看源码
需要代码审计!
$is_upload = false;
$msg = null;

if(isset($_POST["submit"])){
    $ext_arr = array("jpg","png","gif");
    $file_name = $_FILES["upload_file"]["name"];
    $temp_file = $_FILES["upload_file"]["tmp_name"];
    $file_ext = substr($file_name,strrpos($file_name,".")+1);
    $upload_file = UPLOAD_PATH . "/" . $file_name;

    if(move_uploaded_file($temp_file, $upload_file)){
        if(in_array($file_ext,$ext_arr)){
             $img_path = UPLOAD_PATH . "/". rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             $is_upload = true;
        }else{
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = "上传出错!";
    }
}
解题思路

unlink() 函数删除文件。

这里先将文件保存在服务器中,再判断后缀名,若后缀名不合法则删除文件

通过条件竞争的方式在unlink之前,访问上传文件。

利用条件竞争删除文件时间差绕过。

解题步骤

burp suite抓上传info17.php文件的数据包,进行大量重放

Intruder设置:

然后不断在浏览器中访问,发现通过竞争可以访问到上传文件:

Pass-18 提示与查看源码
需要代码审计!
//index.php
$is_upload = false;
$msg = null;
if (isset($_POST["submit"]))
{
    require_once("./myupload.php");
    $imgFileName =time();
    $u = new MyUpload($_FILES["upload_file"]["name"], $_FILES["upload_file"]["tmp_name"], $_FILES["upload_file"]["size"],$imgFileName);
    $status_code = $u->upload(UPLOAD_PATH);
    switch ($status_code) {
        case 1:
            $is_upload = true;
            $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
            break;
        case 2:
            $msg = "文件已经被上传,但没有重命名。";
            break; 
        case -1:
            $msg = "这个文件不能上传到服务器的临时文件存储目录。";
            break; 
        case -2:
            $msg = "上传失败,上传目录不可写。";
            break; 
        case -3:
            $msg = "上传失败,无法上传该类型文件。";
            break; 
        case -4:
            $msg = "上传失败,上传的文件过大。";
            break; 
        case -5:
            $msg = "上传失败,服务器已经存在相同名称文件。";
            break; 
        case -6:
            $msg = "文件无法上传,文件不能复制到目标目录。";
            break;      
        default:
            $msg = "未知错误!";
            break;
    }
}

//myupload.php
class MyUpload{
......
......
...... 
  var $cls_arr_ext_accepted = array(
      ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
      ".html", ".xml", ".tiff", ".jpeg", ".png" );

......
......
......  
  /** upload()
   **
   ** Method to upload the file.
   ** This is the only method to call outside the class.
   ** @para String name of directory we upload to
   ** @returns void
  **/
  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" );
  
  }
......
......
...... 
};

网页上显示的代码并不全!!!需要去WWW文件夹中查看

解题思路

对文件后缀名做了白名单判断,然后检查文件大小、文件是否存在等等。

将文件上传后,对文件重新命名,同样存在条件竞争的漏洞。

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

初始文件命名规则$this->cls_upload_dir . $this->cls_filename,重命名规则$this->cls_upload_dir . $this->cls_file_rename_to,当大量文件需要被重命名时就会出现条件竞争

move在rename之前,move操作进行了一次文件保存, 然后rename进行了一次更改文件名。

利用burp不间断地发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,从而上传成功。

查看配置mime.types,这里记录着可被Apache服务器所识别的文件属性:

#application/x-7z-compressed            7z

7z后缀并不被Apache服务器所识别,却在上传文件名后缀的白名单中,可以利用Apache的解析漏洞将上传的7z后缀文件当做php文件解析。

Apache的解析漏洞

Apache服务器在解析多后缀文件名的文件时,会从后往前辨别后缀,一直辨别到可以解析的后缀。

解题步骤

不停发包方法同上题

观察文件夹中上传文件夹的名字:

也可根据源代码中的命名规则不停地访问文件,直到返回结果:

Pass-19 提示与查看源码
本pass的取文件名通过$_POST来获取。
$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 = $_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 . "文件夹不存在,请手工创建!";
    }
}
解题思路

黑名单策略,文件名用户可控,文件命名upload-19.php.绕过

解题步骤

Pass-20 提示与查看源码
Pass-20来源于CTF,请审计代码!
$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 = "请选择要上传的文件!";
}
解题思路

文件命名规则:$file_name = reset($file) . "." . $file[count($file) - 1];

reset():将内部指针指向数组中的第一个元素,并输出。

end():将内部指针指向数组中的最后一个元素,并输出。

$file = empty($_POST["save_name"]) ? $_FILES["upload_file"]["name"] : $_POST["save_name"];如果save_name不为空则filesave_name,否则filefilename

if (!is_array($file))判断如果file不是数组则以"."分组

文件名命名规则$file_name = reset($file) . "." . $file[count($file) - 1];

我们POST传入一个save_name列表:["info20.php", "", "jpg"],此时empty($_POST["save_name"]) 为假则filesave_name,所以由$ext = end($file);jpg可以通过后缀名判断(判断结束后最后一个元素jpg弹出),并且最终文件名组装为upload20.php.

解题步骤

上传包数据为:

访问:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31708.html

相关文章

  • 基于 Swoole 构建的 CTF AWD 比赛环境搭建与实践

    摘要:出题思路本次题目来自于我的一个外包项目实践。回合制比赛以分钟为一回合。选手在比赛过程中需要盗取到对手的密钥,以窃听他方内容,并修改己方密钥以放泄露动态该部分同样由中心服务器与完成。 Author: RytiaDate: 20190427Blog: www.zzfly.net本人才学疏浅,望君不吝赐教 背景 受学校老师邀请,为学弟学妹举办分享会介绍 AWD 相关经验,本人一时头脑风暴采用...

    GitCafe 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<