upload-labs通关记录

运行:docker info
搜索upload-labs:docker search upload-labs
在这里插入图片描述建立镜像:docker pull c0ny1/upload-labs
查看镜像:docker images
在这里插入图片描述
运行镜像:docker run -ti -d --name upload-labs -p 32770:80 aa4fdd1dd211
(-p 小写p表示docker会选择一个具体的宿主机端口映射到容器内部开放的网络端口上
容器upload-labs启动时使用了-p,选择宿主机具体的32770端口映射到容器内部的80端口上)
在这里插入图片描述
查看正在运行的镜像:docker ps
在这里插入图片描述启动容器:docker start CONTAINER ID
停止容器:docker stop CONTAINER ID
upload-labs通关记录_第1张图片webshell原理:
向服务器上传shell,客户端通过远程连接,利用shell连接到服务器并对服务器进行操作。

Pass-01

直接上传php文件后发现,要求上传jpg或png或gif,不能上传php
upload-labs通关记录_第2张图片再查看提示后发现是js进行前端验证上传的文件类型
upload-labs通关记录_第3张图片所以上传1.php文件时要先上传修改后缀为1.jpg的文件,然后通过抓包再修改为1.php进行绕过
upload-labs通关记录_第4张图片在图片位置右击查看
upload-labs通关记录_第5张图片成功
upload-labs通关记录_第6张图片

Pass-02

上传php文件发现,文件类型错误
upload-labs通关记录_第7张图片查看提示,发现要在服务端对数据包的MIME进行检查
MIME是指文件的后缀
upload-labs通关记录_第8张图片
再查看源码

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

查看源码后发现第五行$_FILES['upload_file']['type']
是用来判断文件MIME类型是否为jpeg或png或gif
这里要知道MIME类型的的值的获取是通过HTTP请求字段里的Content-Type字段

if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))

上传2.php文件抓包后发现,
Content-Type: application/octet-stream
Content-Type就是要判断的类型
将其修改为 image/jpeg

upload-labs通关记录_第9张图片同样右击查看图像,成功
upload-labs通关记录_第10张图片

Pass-03

上传php文件时发现,不允许上传.asp.aspx.php.jsp后缀文件
upload-labs通关记录_第11张图片
提示也为:禁止上传.asp|.aspx|.php|.jsp后缀文件
upload-labs通关记录_第12张图片它只是限制了这几种文件的上传,其他的没有限制
也就是说这里的几种文件被列入服务器上传文件检测的黑名单,只要是这几种就不能上传。

对于黑名单绕过可以采取以下几种方法:

  1. 大小写绕过,例如将Asp,Php,aSpx…
  2. 在文件名后加上.或者空格(需要在Burp中修改)
  3. 黑名单没有的文件类型,例如asa或cer
  4. 修改php后缀为php3,php5(后面的3,5是php的版本,高版本会向低版本兼容)

但是经过测试后发现方法1,2并不能上传,只有3,4能够上传
cer文件上传后查看图片会让下个证书
upload-labs通关记录_第13张图片
但是。。其他上传成功之后查看图片信息都没有显示???

百度

然而asa文件没有显示还未解决,php3,php5不显示是因为配置文件(C:UsersxxxxxDesktopupload-labs-envApacheconfhttpd.conf)中要进行如下配置:
AddType application/x-httpd-php .php .php3 .phtml

但是。。。我没找见。。

最后查看波源码

$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
同样php文件不能上传
查看提示
upload-labs通关记录_第14张图片这么多不能上传。。
查看源码

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

Pass-05
直接查看提示,发现.htaccess也被列入黑名单,不能上传
upload-labs通关记录_第15张图片
尝试黑名单绕过的前两种方法:大小写,文件名后加.或空格

5.PHp上传成功
upload-labs通关记录_第16张图片
upload-labs通关记录_第17张图片
测试后发现文件名后加.和空格并不能上传

查看源码,如果与pass-04相比,会发现没有对大写的限制
就是没有这个
$file_ext = strtolower($file_ext); //转换为小写

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

Pass-06

依旧是黑名单限制
upload-labs通关记录_第18张图片任然是尝试黑名单绕过的前两种方法:大小写,文件名后加.或空格

upload-labs通关记录_第19张图片尝试后发现大小写和文件名后加.不能上传

文件名后加上空格后可以上传

查看源码,与pass-04相比,会发现没有对文件名后空格的限制
即没有
$file_ext = trim($file_ext); //收尾去空

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

pass-07

查看提示,禁止上传所有可解析的后缀

upload-labs通关记录_第20张图片
不会。。
查看源码

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

再次与Pass-04相比,会发现没有对文件名末的.的限制

$file_name = deldot($file_name);//删除文件名末尾的点

pass-08

同样查看提示
upload-labs通关记录_第21张图片再查看源码

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

与pass-04比较的,会发现没有对
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
的限制,可以再在文件名后缀加::$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。

pass-09

提示只允许上传.jpg|.png|.gif后缀的文件upload-labs通关记录_第22张图片

$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_name,而不是$file_ext,也就是说最后保存文件的时候没有重命名而使用的原始的文件名,那相当于$file_name只经过那两段代码的过滤

$file_name = trim($_FILES['upload_file']['name']);//移除字符串两侧的空白字符
$file_name = deldot($file_name);//删除文件名末尾的点

上传时,代码会先将末尾的.去除,剩余.+空格,利用Windows系统文件命名规则,windows会忽略文件末尾的.空格,这样即可上传

pass-10

提示
upload-labs通关记录_第23张图片
源码

$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() 函数是替换字符串中的一些字符

抓包后发现10.php变成了10.
upload-labs通关记录_第24张图片需要双写文件名绕过10.pphphp
注意是pphpphp,而不是phpphp
upload-labs通关记录_第25张图片
pass-11

提示上传路径可控
upload-labs通关记录_第26张图片源码

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

save_path是可控的,利用%00截断来绕过

截断上传的原理:

在url中%00表示ascii码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束,而忽略后面上传的文件或图片,只上传截断前的文件或图片

将路径改为path="upload/11.php%00",那么拼接之后,文件上传时就变成了
"upload/11.php%11.jpg",这时上传便将11.php上传进去,而11.jpg则被截断

还需注意要进行配置才能进行%00绕过

截断条件:

php版本要小于5.3.4
php.ini文件magic_quotes_gpc需要为Off状态

pass-12

同样上传路径可控

$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相比不同在于$_POST['save_path']save_path是通过post方式传的

同样是通过%00截断绕过,但是不能直接抓包在后面加上%00,因为post不会像get一样对%00进行自动解码

要在burp中Hex二进制修改,将70 68 70后面的2b改为00即可绕过

pass-13

upload-labs通关记录_第27张图片提示
upload-labs通关记录_第28张图片源码

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 = "上传出错!";
        }
    }
}

因为只通过读文件的前2个字节判断文件类型
所以在在php文件中就写入
upload-labs通关记录_第29张图片修改为gif上传

上传成功但是无法查看
在这里插入图片描述这里要注意它的文件包含
upload-labs通关记录_第30张图片进行文件包含
include.php?file=upload/7320200718150134.gif
upload-labs通关记录_第31张图片成功

同样对图片马

copy 13.jpg /b + 13.php /a shell.jpg

进行文件包含

pass-14
upload-labs通关记录_第32张图片

提示使用getimagesize()检查是否为图片文件
upload-labs通关记录_第33张图片
源码

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)){
     
            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 = "上传出错!";
        }
    }
}

同样使用文件包含上传图片马

 $info = getimagesize($filename);
 $ext = image_type_to_extension($info[2]);

本关会通过使用getimagesize()检查是否为图片文件

`getimagesize()` 函数将测定任何 GIFJPGPNGSWFSWCPSDTIFFBMPIFFJP2JPXJB2JPCXBMWBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串

pass-15

提示使用exif_imagetype()检查是否为图片文件
upload-labs通关记录_第34张图片exif_imagetype()读取一个图像的第一个字节并检查其签名。 如果发现了恰当的签名则返回一个对应的常量,否则返回 FALSE。返回值和 getimagesize() 返回的数组中的索引 2 的值是一样的,但本函数快得多

同pass-13上传图片马

pass-16

提示重新渲染了图片
upload-labs通关记录_第35张图片源码

$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";
                $newimagepath = UPLOAD_PATH.$newfilename;
                imagejpeg($im,$newimagepath);
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.$newfilename;
                @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";
                $newimagepath = UPLOAD_PATH.$newfilename;
                imagepng($im,$newimagepath);
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.$newfilename;
                @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";
                $newimagepath = UPLOAD_PATH.$newfilename;
                imagegif($im,$newimagepath);
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.$newfilename;
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
     
            $msg = "上传出错!";
        }
    }else{
     
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}

学习函数

imagecreatefromgif():创建一块画布,并从 GIF 文件或 URL 地址载入一副图像
imagecreatefromjpeg():创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像
imagecreatefrompng():创建一块画布,并从 PNG 文件或 URL 地址载入一副图像
imagecreatefromwbmp():创建一块画布,并从 WBMP 文件或 URL 地址载入一副图像
imagecreatefromstring():创建一块画布,并从字符串中的图像流新建一副图像
move_uploaded_file() 函数将上传的文件移动到新位置。

看了看师傅们的博客
https://xz.aliyun.com/t/2657#toc-2

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 = '上传出错!';
    }
}

这一段代码大体意思是先将文件上传到服务器,再判断后缀名,如果合法则保留下来,如果不合法,则删除在服务器的文件

 $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);

unlink() 函数是删除文件

根据代码可以通过条件竞争的方式在unlink()函数删除之前,访问上传文件

先来了解一下条件竞争

条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。

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

burp抓包在Intruder下选择Payload typeNull payloads就是无payload,可以无限上传
同时勾选Continue indefinitely
upload-labs通关记录_第36张图片线程设置为5,访问1000次
upload-labs通关记录_第37张图片200的返回码说明已经成功

upload-labs通关记录_第38张图片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" );
  
  }
......
......
...... 
};

先是 $ret = $this->move();,进行了一次文件保存,然后再 $ret = $this->renameFile();,进行了一次更改文件名,同样可以用条件竞争进行绕过

pass-19

提示为取文件名通过$_POST来获取
upload-labs通关记录_第39张图片

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

参考了师傅们的博客

move_uploaded_file会忽略掉文件末尾的/.
而且文件名是从$_FILES['upload_file']['tmp_name']中获取的,所以用户是可以进行控制的,所以通过/. 来进行绕过
因为文件名可控或者用move_uploaded_file函数的00截断漏洞绕过

pass-20

提示要代码审计

源码

$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];处理
上传数组的话会跳过$file = explode('.', strtolower($file));
后缀有白名单过滤

$ext = end($file);
$allow_suffix = array('jpg','png','gif');

最终的文件名后缀取的是$file[count($file) - 1]

让$file为数组。$file[0]为20.php/,也就是reset($file),
然后再令$file[2]为白名单中的jpg。此时end($file)等于jpg,$file[count($file) - 1]为空。
而 $file_name = reset($file) . '.' . $file[count($file) - 1];,也就是20.php/.最终move_uploaded_file会忽略掉/.最终上传20.php

说实话这一部分没理解。。

附上师傅的博客
https://xz.aliyun.com/t/4029#toc-16

结语:做的老垃圾了,时间也一直拖

你可能感兴趣的:(upload-labs通关记录)