一、文件上传漏洞介绍
Web应用程序通常带有文件上传的功能,比如在博客园发表文章需要上传图片等行为,这其中就可能存在文件上传漏洞。
如果Web应用程序存在上传漏洞,攻击者可以直接上传WebShell(以asp、php或者jsp等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门)到服务器上。
二、解析漏洞
攻击者在利用上传漏洞时,通常会与Web容器的解析漏洞配合在一起。
常见的Web容器有IIS、Nginx、Apache、Tomcat。
1. IIS解析漏洞(基于IIS6.0
环境)
(1)当建立*.asa
、*.asp
格式的文件夹时,其目录下的任意文件都将被IIS当做asp文件来解析。
测试案例:建立文件夹parsing.asp
,在文件夹内新建内容为<%=NOW()%>
的文本文件test.txt
,然后在浏览器里访问。
测试结论:
NOW()
是ASP获取当前时间的函数。正常而言,IIS是不会去解析文本文档格式而是直接显示内容。而在parsing.asp
文件夹内,却被当做ASP脚本来解析。
(2)当文件为*.asp;1.jpg
时,IIS同样会以ASP脚本执行。
测试案例:创建文件名为test.asp;1.jpg
,其内容为<%=NOW()%>
。
(3)WebDav漏洞
WebDav是一种基于HTTP1.1协议的通信协议。在GET、POST、HEAD等HTTP标准方法之外扩展了新方法。
攻击者可以通过PUT方法向服务器上传危险脚本。
2. Apache解析漏洞(基于Apache 1.x
和Apache 2.x
环境)
(1)Apache漏洞介绍:
我们在Apache环境中设置文件内容为<?php phpinfo(); ?>
的1.php.aa
文件,在正常情况下,应该会提示下载该文件,但是在此情况下却显示了phpinfo()的内容。
(2)Apache在解析文件名的原理:
当遇到不认识的扩展名,将会从后向前解析,直到碰到认识的扩展名。例如文件名为1.php.aa
,首先解析aa
扩展名,发现不认识继续向前面遍历。
(3)Apache可识别扩展名(在/conf/mime.types
文件中有详细列表)
(4)对于Apache解析漏洞的防范:
在开发设计上传文件程序时,应设置判断是否为PHP、ASP、ASPX
以及在(3)中提到的扩展名的函数,如果函数返回Fasle则禁止上传。
3. PHP CGI(Nginx)解析漏洞
(1)漏洞介绍:
我们在Nginx环境下布置一个1.jpg
文件,并用文本编辑器写入<?php echo "hello world"; ?>
。通常情况而言,我们打开URL:http://127.0.0.1:80/1.jpg 显示为一张无法识别的图片,但是利用Nginx解析漏洞,我们打开URL:http://127.0.0.1:80/1.jpg/1.php (1.php是虚构、不存在的文件)时,却将文件1.jpg识别为一个php文件。
(2)漏洞解析:
在PHP的配置文件中有一个选项:cgi.fix_pathinfo=True
,在访问URL时,当Nginx服务器遇到1.php是不存在的文件时,便会向前遍历解析。
如下图在php7.3.4版本中,cgi.fix_pathinfo默认是开启的。
三、绕过上传漏洞
使用Web应用中,很多场景都要使用文件上传。文件上传的流程为客户端使用JavaScript验证与服务器端采用随机数命名文件。因此在客户端利用JavaScript在文件未上传的时候就对文件进行验证,服务器端检测上传文件的MIME类型、检测文件扩展名是否合法、是否嵌入恶意代码。
任何客户端验证都是不安全的。客户端验证是防止用户错误输入,减少服务器开销而已。
1. 客户端检测
下面通过对Upload.html
中JavaScript源代码分析来突破客户端验证。
<html>
<head>
<title>Pic Upload</title>
<script type="text.javascript">
funtion 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('file invalid');
}
return flag;
}
</script>
</head>
<body>
<form action="upload.php" method="post" onsubmit=“ checkFile ”
enctype="multpart/form-data">
<input type="file" name="file" id="file" /><br/>
<input type="submit" value="Submit" name="submit" />
</form>
</body>
</html>
由源代码得知客户端JavaScript仅针对png,bmp,gif,jpg四种进行文件上传,那么攻击者就可以将文件名伪装成这些类型上传。
(1)使用Firebug
Firebug介绍:
是网页浏览器 Mozilla Firefox 下的一款开发类扩展,它集HTML查看和编辑、Javascript控制台、网络状况监视器于一体,是开发JavaScript、CSS、HTML和Ajax的得力助手。
Firebug从各个不同的角度剖析Web页面内部的细节层面,给Web开发者带来很大的便利。
Firebug也是一个除错工具。用户可以利用它除错、编辑、甚至删改任何网站的 CSS、HTML、DOM 以及JavaScript 代码。
在Upload.html中单击Submit按钮,Form表单将会触发onsubmit时间从而调用checkFile()函数,通过检验扩展名是否合法从而允许文件是否可以上传至服务器。Firebug将onsubmit事件删除,因此所有类型的文件扩展名都可以绕过JavaScript函数checkfile()验证。
(2)中间人攻击
机制与(1)中的Firebug完全不同,中间人攻击在传输中的HTTP层做手脚。可以简单地理解为上传
1.jpg
文件通过JavaScript函数的验证,再使用抓包工具(Burp Suite)将文件名修改后缀为php。
注意:HTTP请求头部Content-Length参数(实体正文长度),需要根据文件名长度的改动进行相应的修改。
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 => $value) {
if ($value==$extension){
$boo = true;
break;
}
}
if($boo){
echo "file invalid";
}
}
?>
黑名单过滤的缺点:
- 攻击者可以从黑名单中找到Web开发者忽略的扩展名
- 没有进行大小写转换,诸如
.PHP
扩展名不在黑名单里,依旧会被服务器接收 - windows系统下,如果文件名以"."或空格结尾会自动取出。
asp.
转化为asp
白名单过滤:
与黑名单过滤相反,白名单只允许已定义过的扩展名,比黑名单有更好的防御机制。
<?php
$Whitelist = array('jpg','jpge','bmp','gif','png');#白名单允许函数
if(isset($_POST["submit"])){
$name = $_FILES['file']['name'];
$extension = substr(strrchr($name, "."), 1);
$boo = true;
foreach($Blacklist as $key => $value) {
if ($value==$extension){
$boo = false;
break;
}
}
if(!$boo){
echo "file invalid";
}
}
?>
白名单过滤的缺点:
白名单并不能完全防御上传漏洞。在IIS解析漏洞章节中,我们介绍过当文件名为*.asp;1.jpg
时,白名单过滤机制识别文件扩展名为.jpg
,即验证通过可以上传。但是在IIS解析漏洞中,此文件会被当做asp脚本程序来执行。
(2)MIME验证
MIMIE类型用来设定某种扩展名文件的打开方式。
if($_FILES['file']['type']==" image/jpeg"){
$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_uploaded_file($imageName, "./uploadFile/".$imageName;
echo("Success");
}else{
echo("Fail");
exit();
}
上传php文件时,我们使用抓包工具(Burp Suite)查看其MIME类型,可以发现php文件的MIME类型为application/php,无法通过验证。因此可以将Content-Type修改成image/jpeg类型,即可通过验证。
(3)目录验证
简介:在文件上传时,允许用户将文件放到指定的目录中,如果不存在指定目录,就会先创建目录再将文件放入。
攻击手段:HTML代码中有一个隐藏标签<input type="hidden" name="Extension" value="up"/>
,这是文件上传默认的文件夹,我们可以将参数value的值就可以达到目的。结合IIS解析漏洞(当建立*.asa
、*.asp
格式的文件夹时,其目录下的任意文件都将被IIS当做asp文件来解析),就可以将恶意代码写入。
(4)截断上传攻击
此方法通常适用于ASP程序。
//ASP代码
<%
username = request("username")
Response.write username
%>
//将请求输入的username输出
截断上传攻击指的是%00将后面的字符都截断,当我们上传文件名为1.asp x.jpg
时,然后将空格的十六进制数20改成00,因此文件名为1.asp%00x.jpg
,即符合截断上传攻击原型,最终将上传1.asp
文件。
四、文件编辑器上传漏洞
(1)敏感信息暴露
部分文本编辑器的文件目录存在一些敏感文件,将敏感信息直接暴露在攻击者前。
(2)黑名单策略错误
部分文本编辑器采用的是黑名单机制,基于上述黑名单策略的缺点,攻击者仍可以找到另外的危险脚本。
(3)任意文件上传漏洞
五、修复上传漏洞的策略
文件上传漏洞产生的原因主要是:
- 目录过滤不严,攻击者可能建立畸形目录
- 文件未重命名,攻击者可能利用Web容器解析漏洞
经过学习,设计如下代码可预防上述攻击。
<?php
if(!isset($_POST['submit'])){
exit();
}
$arr = Array('jpg','gif','jpeg','png','rar','zip','doc'); //设置白名单
$imageTempName=$_FILES['file']['temp_name'] //将文件放在临时路径,防止解析漏洞发生
$imageName=$_FILES['file']['name'];
$last=strtolower(substr($imageName, strrpos($imageName,'.')+1)); //获取文件扩展名并全部转换成小写
if(!in_array($last,$arr)){
exit('extension name invalid .$last ...'); //当扩展名不在白名单内,立即退出程序。
}
$Extension=$_POST['Extension']; //确认扩展名安全方可获取文件上传目录
$imageName = md5($imageName).".".$last; //对文件重命名,防止利用解析漏洞
move_uploaded_file($imageTempName, "./$Extension/".$imageName);
echo("Success!");
}
- 接收文件将其存放在文件临时路径,即便存在解析漏洞也不能获取有效信息。
- 将扩展名与白名单对照,不符合的情况下直接退出程序。
- 对文件重命名方式诸如
test.asp;1.jpg
文件名绕过白名单却利用解析漏洞实施攻击。