关于文件上传漏洞也是WEB安全常见漏洞之一,但随着开发者安全意识提高,现在很少出现常规的文件上传漏洞,但为了学习,还是要了解下以前常见的文件上传漏洞,这里利用上传漏洞靶机作为演示。
有些文件上传表单,会在前端JS里进行文件的校检,而后端因为前端已经校检了,所以没有在二次校检。
我们知道,只要是客户端限制都能绕过的,所以这种方式限制非常容易绕过:通过抓包修改、开发者工具修改JS代码等等。
多用途互联网邮件扩展类型,浏览器通常使用MIME类型(而不是文件扩展名)来确定如何处理文档,每个MIME类型由两部分组成,前面是数据的大类别,例如声音audio、图象image等,后面定义具体的种类。也就是通过MIME可以确认文件的类型。
常见的MIME类型:
普通文本 .txt:text/plain
超文本标记语言文本 .html:text/html
GIF图形 .gif:image/gif
JPEG图形 .jpeg,.jpg:image/jpeg
如果,文件上传接口后端是通过校检文件的MIME类型,也可以通过抓包修改文件的MIME类型进行绕过即可。
if (($_FILES['upload_file']['type'] != 'image/jpeg') || ($_FILES['upload_file']['type'] != 'image/png') || ($_FILES['upload_file']['type'] != 'image/gif')) {
$msg = '文件类型不正确,请重新上传!';
}
不同的文件都有不同的文件头,文件头就是为了描述一个文件的一些重要的属性,它告诉了打开并处理该文件的程序这些属性。有些上传接口会检测你上传的文件头信息以此来判断是否为正真的文件类型。那么此时只需要在上传的文件开头部分添加图片的文件头即可,这也是制作图片木马的由来,就是为了绕过文件头检测机制和文件大小检测机制。
$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;
}
也有对文件大小进行了限制,如果不达到文件大小的要求,则不能上传,这种破解也很简单,在上传数据里不断填充垃圾数据或制作图片木马进行绕过。
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename); //取得图像大小并返回图像的尺寸以及文件类型
//$info[2] 索引2是图像类型的标记:1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP等等
$ext = image_type_to_extension($info[2]); //取得图像类型的文件后缀
if(stripos($types,$ext)){
return $ext;
}else{
return false;
}
}else{
return false;
}
如果后端是通过黑名单形式校检文件后缀名,也是存在绕过的可能,因为黑名单难免会有遗漏的文件类型发生。
比如:在某些PHP环境中,后缀名为php1、php2、php3、php4、php5、phtml等文件,也能当作php文件执行。如果可执行文件类型都过滤了,也可以上传html网页文件,里面包含钓鱼、xss等代码也能进行利用,所以建议不采用黑名单形式进行校检。
$deny_ext = array('.asp','.aspx','.php','.jsp');
$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)) {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。任何版本的Apache Web服务器都是支持.htaccess的。
知道了这个文件的作用,如果能上传htaccess文件,那么可以通过配置htaccess,使其上传图片文件也能当作可执行文件操作。
$deny_ext = array(".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");
$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)) {
$msg = '此文件不允许上传!';
}
SetHandler application/x-httpd-php
#所有文件都可以当作php来执行
还是黑名单形式限制,如果开发者忘记对文件后缀名进行小写转换,那么可通过大写来进行绕过,如PhP。
$deny_ext = array(".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");
$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)) {
$msg = '此文件不允许上传!';
}
还是黑名单形式限制,如果开发者忘记对文件后缀名进行首尾去空,那么可以在后缀名中加入空格绕过黑名单限制,如1.php 。但是只能在首尾,如果在后缀名中间加入空格则不能当作可执行文件了。
$deny_ext = array(".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");
$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)) {
$msg = '此文件不允许上传!';
}
还是黑名单形式限制,如果开发者忘记对上传文件后缀名进行.号处理,利用windows特性,会自动去掉后缀名中最后的”.”,那么可在后缀名中加”.”进行绕过。
$deny_ext = array(".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");
$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)) {
$msg = '此文件不允许上传!';
}
还是黑名单形式限制,如果开发者忘记对上传文件后缀名进行::$DATA处理,利用windows的NTFS文件流特性,可在后缀名中加” ::$DATA”进行绕过。
$deny_ext = array(".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");
$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)) {
$msg = '此文件不允许上传!';
}
还是黑名单形式限制,这里用str_ireplace()函数,对黑名单中的文件类型替换成空,但是我们可以利用双写绕过这个限制,如1.pphphp,替换php为空,则剩下为php了。
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
这次是根据白名单形式进行限制,也是大多数开发者采用的措施。如果文件保存的路径是采用拼接方式进行的(可自定义文件保存路径或不重命名文件名),那么可导致通过截断攻击绕过限制。
$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;
}
$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;
}
首先理解下截断的原理,在C/PHP等语言中,截断的核心,就是chr(0)这个字符,这个字符不为空(Null),也不是空字符(""),更不是空格!当程序在输出含有chr(0)变量时,chr(0)后面的数据会被停止,换句话说,就是误把它当成结束符,后面的数据直接忽略,这就导致漏洞产生。
那么 %00截断和0x00截断有啥区别呢?其实他们的截断原理都一样,只是在GET请求中,%00会被自动解码,而在POST请求中,不会自动解码,故在POST请求中,我们需要手动对它的十六进制改写为0x00。
比如:upfilename=path & file
如果path可控,那么upfilename=upload/1.php%00 & 1.jpg => upfilename=upload/1.php
如果file可控,那么upfilename=upload/ & 1.php%00.jpg => upfilename=upload/1.php
如果开发者设置的是先上传文件进行保存,在验证文件,如果不通过则删除文件。这样就可以利用批量提交请求,使其服务器端在删除文件之前执行木马。(和删除文件进行竞争,利用删除文件时间差)。
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_ADDR . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = $UPLOAD_ADDR . '/'. 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 = '上传失败!';
}
}
如果WEB服务器存在解析漏洞,那么也可以利用文件解析漏洞绕过文件上传限制。
目录解析漏洞:
xx.asp/xx.jpg 服务器会把xx.asp目录下的文件解析成asp文件
文件解析漏洞:
xx.asp;.jpg 服务器默认不解析;后的内容,因此xx.asp;.jpg 变成了xx.asp文件了
IS7/7.5在Fast-CGI运行模式下,在一个文件路径(/xx.jpg)后面加上/xx.php会将/xx.jpg/xx.php 解析为 php 文件。
低版本的Apache解析文件的规则是从右到左开始判断的,如果遇到后缀名为不可解析的,就在往左判断。利用这个特性,可以上传如xx.php.a。
这个和iis7.5的畸形解析漏洞类似,都是由于开启了Fast-CGI模式,导致在一个文件路径(/xx.jpg)后面加上/xx.php会将/xx.jpg/xx.php 解析为 php 文件。
这在上面已讲过:
xx.php.
xx.php(空格)
xx.php::$DATA
故总结:遇到文件上传时,首先判断是根据黑名单还是根据白名单进行限制的,如果是根据黑名单形式,那么就容易多,一般存在风险点。如果是白名单形式限制,测试截断漏洞、文件解析漏洞等。