upload-labs通关解析


upload-labs 文件上传漏洞原理


本文源码可能与网上的不太一样导致关卡有些错乱,如果你的第五关和我的不一样,那么我的第5关就是你的第6关,依次类推到第9关

产生条件

1.服务器配置不当会导致任意文件上传
2.web应用开放了文件上传的功能,并对上传的文件没有进行足够的限制和过滤
3.web应用开放了文件上传功能,虽然在开发时加入了一定的过滤功能,但并不严格,可以被绕过
4.上传文件时如果服务端代码未对客户端上传的文件进行严格的验证和过滤,就容易造成可以上传任意文件的情况,包括上传脚本文件(asp、aspx、php、jsp等格式的文件)。

webshell

恶意的脚本文件,又被称为webshell,webshell脚本称为一种网页后门,webshell脚本具有非常强大的功能,比如查看服务器目录、服务器中的文件、执行系统命令

upload-labs Pass-01


第一关上传文件只允许jpg、png、gif后缀的文件

//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";

我们可以写个一句话木马把后缀名改成.jpg上传并用bp抓包并将文件名改回.php就可以发现我们绕过了检查
具体如下图:


然后用蚁剑连接就行

upload-labs Pass-02


第二题仍然可以用第一题的方法做,这里用方法2:
第二关提示中说对MIME进行检查
什么是MIME?使用MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准,使用MIME类型可以设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。
常见MIME类型如下:

上传一个1.php文件将Content-Type:改为image/png

upload-labs Pass-03 黑名单


基于黑名单验证:只针对黑名单中没有的后缀名,文件才能上传成功。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $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)) {
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR. '/' . $_FILES['upload_file']['name'])) {
                 $img_path = $UPLOAD_ADDR .'/'. $_FILES['upload_file']['name'];
                 $is_upload = true;
            }
        } else {
            $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

定义一组后缀名,然后对于传输过去的文件,经过删除文件名末尾的点,从文件的点开始向后截取,
以及将文件名全部转化成小写,最后移除旁边的空格。
其中strrchr()函数的作用就是搜素并窃取搜索点及以后的位置
他禁用了挺多的类型的后缀的但是,我们还可以使用其他后缀进行绕过
phtml,php3,php4,php5,pht
这里就不示范了

upload-labs Pass-04


第四关黑名单爆增,这里通过写一个.htaccess文件绕过,htaccess文件时Apache服务器中的一个配置文件,负责相关目录下网页配置,可以帮我们实现网页301重定向,自定义404错误页面,改变文件扩展名等功能
我们构造一个.htaccess文件,里面写上代码

<FilesMatch "shuaige">
SetHandler application/x-httpd-php
</FilesMatch>

这串代码的作用是将上传的文件都作为.php来看,先将他上传上去,在将我们的1.png上传上去就可以绕过检查

upload-labs Pass-05


源码如下:

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $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 = 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)) {
            if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
                $img_path = $UPLOAD_ADDR . '/' . $file_name;
                $is_upload = true;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
    }
}

方法一:

可以看到它过滤了.和空格,但只过滤了1次我们可以构造1.php. .来绕过

方法二:

可以看到过滤了.htaccess文件,这里可以构造php.ini 文件,php.ini 是 php的配置文件,.user.ini 中的字段也会被 php 视为配置文件来处理,从而导致 php 的文件解析漏洞。

条件:
  • 服务器脚本语言为PHP
  • 服务器使用CGI/FastCGI模式
  • 上传目录下要有可执行的php文件
    我们需要在文件里写上auto_prepend_file=1.jpg
    .user.ini文件里的意思是:所有的php文件都自动包含1.jpg文件。.user.ini相当于一个用户自定义的php.ini
    接着上传一个1.jpg文件包含一句话木马
    我们要改一下php-ini配置文件

    改为10这样就不用等5分钟了,然后在复制图像地址后,用蚁剑访问将文件名改为readme.php

upload-labs Pass-06


if (file_exists($UPLOAD_ADDR)) {
        $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 = 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 

这里没有对空格进行删除,上传.php后,用bp后进行抓包修改后缀为.php 就行了

upload-labs Pass-07


if (file_exists($UPLOAD_ADDR)) {
        $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 = 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); //首尾去空

这里没有对点进行删除,上传.php后,用bp后进行抓包修改后缀为.php.就行了

upload-labs Pass-08


if (isset($_POST['submit'])) {
    if (file_exists($UPLOAD_ADDR)) {
        $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 = 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); //首尾去空

这里没有对::$DATA进行删除,上传.php后,用bp后进行抓包修改后缀为.php::$DATA就行了

window特性

在window的时候如果文件名+”::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

  • 例如:”phpinfo.php::$DATA”Windows会自动去掉末尾的::$DATA变成”phpinfo.php”

upload-labs Pass-09

和第五题是一样的

upload-labs Pass-10白名单

$file_name = str_ireplace($deny_ext,"", $file_name);\\将定义的后缀名改为空格

上传11.php,那么他就会把你的php过滤掉。文件没有了后缀名,自然也就无法解析了。但是他是一次过滤,也就是说我们写两个php就可以了抓包改为:10.pphphp,他过滤掉一个,正好剩下了11.php

upload-labs Pass-11

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;

img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;这句是用get传参,这里是一个白名单,只允许上传’jpg’,’png’,’gif’格式的文件,但是上传路径是可以控制的,可以使用%00进行截断。%00只能用于php版本低于5.3的。这里我们需要把phpstudy切换一下版本,把magic_quotes_gpc调为off

上传即可

upload-labs Pass-12

这题和11关差不多,只是用了post传参

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;

post不会自行解码,我们需要对%00进行编码,对%00选中右击:


发送即可

upload-labs Pass-13


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

第十三关,用图片+php代码,组成一个图片码进行上传,当然要想解析出来这个图片,还得有这个包含漏洞。这里一定要用一个真正的图片,只有用相同类型的图片造相同类型的图片
这里附上图片码制作链接
文件对自身文件内容,有着自己的文件头标识,需要文件转为16进制,然后看各个文件类型对文件头的定义,就可以知道文件的类型了

1.BMP
-文件头标识 (2 bytes) 42 4D
2.PNG
- 文件头标识 (8 bytes) 89 50 4E 47 0D 0A 1A 0A
3.GIF
- 文件头标识 (6 bytes) 47 49 46 38 39(37) 61
4.JPEG/JPG
- 文件头标识 (2 bytes): FF, D8 (SOI) (JPEG 文件标识)

这里用fopen函数打开图片文件用rb(二进制打开),他只读两个字节 unpack从二进制字符串对数据进行解包,加@的原因就是让其怎么都可以执行不报错 ,而这里的unpack的意思是 将$bin中的以二进制的形式解包输出到chars中。 而下面这个intval是转化十进制的意思,将本来输出的二进制再转化为十进制,接下来就是进选择判断了。
把制作好图片码上传上去,点击黄色的“文件包含漏洞”,并构造如图的url即可

正常可以看到一堆乱码:

也可以用这个URL链接蚁剑

upload-labs Pass-14


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;
    }
}
  • getimagesize() 函数:将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。没什么变化用和上题的方法就行。

upload-labs Pass-15


$image_type = exif_imagetype($filename);

exif_imagetype() 读取一个图像的第一个字节并检查其签名。
本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 $_SERVER[‘HTTP_ACCEPT’] 结合使用来检查浏览器是否可以显示某个指定的图像。更改phpstudy的配置文件php.ini更改方法
之后和13关一样

upload-labs Pass-16 二次渲染


二次渲染原理

  • imagecreatefromjpeg()函数,二次渲染是由Gif文件或 URL 创建一个新图象。成功则返回一图像标识符/图像资源,失败则返回false,导致图片马的数据丢失,上传图片马失败。

解题

把原图和他修改过的图片进行比较,看看哪个部分没有被修改。将php代码放到没有被更改的部分,配合包含漏洞,就可以了。

如图两段是没有改变过的一段区域,(左边的图被我改成了一句话木马),在原图该区域写上一句话木马即可。
具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的(我就失败了很多次),知道怎么解就可以了

upload-labs Pass-17 条件竞争


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

同样这里先判断白名单,然后获取文件名和并截取后缀名,$upload_file = UPLOAD_PATH . '/' . $file_name;这一句是上传目录的名称和已经截取到的后缀名,接着上传这个文件,这里就有问题了他是先上传文件后判断这个文件名合不合法,如果不合法将其删掉,可是这段代码的执行还是有时间的,虽然很短但是只要我们上传的足够多次且够快,在它删掉之前就可以访到它问,这就相当于我们打开了一个文件,然后再去删除这个文件,就会提示这个文件在另一程序中打开无法删除。
下面演示操作:
上传一个一句话木马的php文件后,bp抓包




出现200算成功了,这时将upload文件夹打开可以看到我们上传的文件一直闪烁,出现又消失。

upload-labs Pass-18


这关和上关差不多但是有点不一样的是它先检查后上传,也就是我们的上传得符合白名单,于是我们可以用图片木马来做,可以用之前的关卡验证是否木马有效。

upload-labs Pass-19


这关对上传的文件没有检查,但是会对我们保存的文件名进行黑名单检查,而move_uploaded_file()有这么一个特性,会忽略掉文件末尾的 /.(前面一直没说,他会用第一个参数的名字,修改第二个参数,也就是保存路径的最终文件名字,而这个改变会将/.忽略),我们在save_name中把后缀名加上/.就可以绕过

upload-labs Pass-20


做这关前先看看这几个函数:

  • explode(separator,string[,limit]) 函数,使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
  • end(array)函数,输出数组中的当前元素和最后一个元素的值。
  • reset(array)函数,把数组的内部指针指向第一个元素,并返回这个元素的值
  • count(array)函数,计算数组中的单元数目,或对象中的属性个数
    这关是先判断mime类型合不合法,如果判断文件名不是数组,再将文件名字进行拆分也就是explode函数,接着判断黑名单文件名
    $file_name = reset($file) . '.' . $file[count($file) - 1];重点是这句话,它将前面切分的文件进行组合,也就是数组的第一个元素和倒数第二个元素组合而成新的file_name,接着正常上传

    上面遗漏了对mime类型的更改,我们上传save_name[0]、save_name[2]是先绕过判断,也就是不用explode拆分了,然后相当于告诉它这个数组有3个元素,且save[1]为空没设置值,直接用save[0]将我们要的文件名上传就行了

文章作者: 矢坕
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 矢坕 !
  目录