前言
我是根据upload-lab来总结文件上传漏洞。
思维导图:
pass-01
提示:在客户端使用js对不合法图片进行检查!
所以我们可以通过抓包,改包就可以绕过。。。
pass-02
提示:服务端对数据包的MIME进行检查!
这里是对Content-Type: 进行验证。所以改一下
Content-Type: image/jpeg
就可以上传上webshell。
pass-03
提示:禁止上传.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: php2、php3、php5、phtml、pht
ASP: aspx、ascx、ashx、cer、asa
JSP: jspx
我的是php环境,上传别名后发现文件上传成功,但是不能被apache解析
查资料发现需要修改httpd.conf:(#号是注释符)
AddType application/x-httpd-php .php .phtml .php3 .php4 .php5 .php2
这样apache才会把文件解析成php。
pass-04
提示:禁止上传.php|.php5|.php4|.php3|.php2|php1|.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|.sWf|.swf后缀文件!
这里的黑名单比较全了,但是它少了.htaccess
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。
也就是说,我们能控制这个目录的解析方式。
这里我们可以上传文件名为.htaccess的文件,里面写上:
SetHandler application/x-httpd-php
这是把目录下的文件当作php来解析。
这时我们可以上传一个图片马,就可以当做php文件来解析。。。
即weshell
pass-05
这一关增加了.htaccess限制。
查看源码:
$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 = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
发现他没有忽略大小写。。这时我们就可以通过大小写绕过。
pass-06
这关同上关一样,黑名单很全。需要查看源码:
$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 = $_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
发现没有去除文件名末尾的空格。这里就可以在文件名后加空格来绕过过滤。。。
pass-07
查看源码:
$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_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
发现没有删除文件末尾的点。然后就是windows操作系统的一个特性:
在windows操作系统中,会自动去掉后缀名中的“.”
所以我们可以通过在文件名后加点来绕过过滤。。。
pass-08
查看源码:
$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 = trim($file_ext); //首尾去空
发现缺少去除::$DATA。
这是windows系统的一个特性:
所以这里我们可以在文件名后面加上::$DATA来绕过过滤。。
pass-09
$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); //首尾去空
这里,先去掉文件末尾的点再去空格。然而只去掉一次。所以可以使用文件名后加点空格点的方式来绕过。。。
pass-10
这关对文件名进行去除可解析后缀替换成空。。然而只替换一次。。。所以这里就存在双写后缀绕过过滤。
pass-11
这关提示文件路径可控。
这里指的是move_uploaded_file
中的save_path
可控。这里存在%00截断.
这里有版本限制:5.4.x<= 5.4.39, 5.5.x<= 5.5.23, 5.6.x <= 5.6.7
我没有环境,没有得到webshell。
pass-12
方式同上。。。只是请求方式变了。
pass-13
提示:
本pass检查图标内容开头2个字节!
只需在图片末尾加上木马就行。。
pass-14
提示:
本pass使用getimagesize()检查是否为图片文件!
这样上一关的方式就不行了。
需要我们制作图片马。
在cmd下执行:
copy 图片名/b+马文件名 生成图片马名
就可得到图片马。上传即可。
pass-15
提示:
本pass使用exif_imagetype()检查是否为图片文件
方法同上
pass-16
这关对图片进行了二次渲染
gif
只需要找到渲染前后没有变化的位置,然后将php
代码写进去,就可以了。
jpg图片可以通过脚本进行实现。
命令:
使用脚本处理1.jpg,命令php jpg_payload.php 1.jpg
png请参考学长的文章:
二次渲染绕过
pass-17
$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文件。
抓包。
一边上传文件
一边访问文件
最后成功执行命令:
pass-18
方式同上。
Pass-19
利用文件名加点来绕过检测
Pass-20
$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 = "请选择要上传的文件!";
}
可以发现$file_name
经过reset($file) . '.' . $file[count($file) - 1];
处理。
如果上传的是数组的话,会跳过$file = explode('.', strtolower($file));
。并且后缀有白名单过滤.
而最终的文件名后缀取的是$file[count($file) - 1]
,因此我们可以让$file
为数组.
$file[0] = alex.php
$file[2] = jpg
我们没有定义$file[1],所以count($file)等于2,那么$file[count($file)-1]就为空,最后文件名就是$file[0]。
总结:
姿势很多。还需努力…