一般防止上傳漏洞手法
1、客戶端檢測(cè):客戶端使用JavaScript檢測(cè),在文件未上傳時(shí),就對(duì)文件進(jìn)行驗(yàn)證
//任何客戶端的驗(yàn)證都是不安全的,客戶端驗(yàn)證目的是防止用戶輸入錯(cuò)誤、減少
//服務(wù)器開(kāi)銷,而服務(wù)端驗(yàn)證才可以真正防御攻擊者。
2、服務(wù)器端檢測(cè):服務(wù)端腳本一般會(huì)檢測(cè)文件的MIME類型,檢測(cè)文件擴(kuò)展名是否合法
客戶端檢測(cè)
客戶端驗(yàn)證代碼形如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<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>
<form 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(date('Y-m-d h:m:s')).strrchr($name,".");
$size = $_FILES['file']['size'];
$tmp = $_FILES['file']['tem_name'];
move_uploaded_file($tmp,$name);
echo "文件上傳成功!path:".$name;
}
?>
繞過(guò):
1、可以用firebug將form表單中的onsubmit事件刪除,這樣就可以繞過(guò)驗(yàn)證。
2、使用Burp Suite:
1)先將木馬文件的擴(kuò)展名改為一張正常圖片的擴(kuò)展名,如jpg
2)上傳時(shí)使用Burp Suite攔截?cái)?shù)據(jù)包,將木馬文件擴(kuò)展名改為php就可繞過(guò)客戶端驗(yàn)證。
注意:這里修改文件名字后,請(qǐng)求頭中的Content-Length的值也要改
服務(wù)端檢測(cè)
服務(wù)端分為6項(xiàng):
* 黑名單與白名單驗(yàn)證
* MIME驗(yàn)證
* 目錄驗(yàn)證
* 截?cái)嗌蟼鞴?* .htaccess文件攻擊
* 檢測(cè)文件內(nèi)容
黑名單與白名單驗(yàn)證
- 黑名單過(guò)濾方式
<?php
$Blacklist = array('asp','php','jsp','php5','asa','aspx'); //黑名單
if (isset($_POST["submit"])){
$name = $FILES['file']['name']; //接收文件名
$extension = substr(strrchr($name, ".") , 1); //得到擴(kuò)展名
$boo = false;
foreach ($Blaklist as $key => $value){
if ($value==$extension) { //迭代判斷是否命中
$boo = true;
break; //命中后直接退出循環(huán)
}
}
if (!$boo) { //若沒(méi)有被命中,則進(jìn)行上傳操作
$size = $_FILES['file']['size']; //接收文件大小
$tmp = $FILES['file']['temp_name']; //臨時(shí)路徑
move_uploaded_file($tmp, $name); //移動(dòng)臨時(shí)文件到當(dāng)前文件目錄
} else {
echo "文件不合法!!";
}
}
?>
對(duì)于上面的過(guò)濾可以通過(guò)如下方法繞過(guò):
- 從黑名單中找到web開(kāi)發(fā)者忽略的擴(kuò)展名,如:cer
- 沒(méi)有對(duì)擴(kuò)展名進(jìn)行大小寫轉(zhuǎn)換,在window平臺(tái)依然可以大小寫繞過(guò)
- 在window下,若文件名以"."或者空格作為結(jié)尾,系統(tǒng)會(huì)自動(dòng)去除"."與空格,
所以可以上傳以“asp.”和“asp_”為擴(kuò)展名的文件 - 0x00截?cái)嗬@過(guò)
- 解析漏洞
* 白名單過(guò)濾方式
> ```php
<?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_uploaded_file($tmp,$name);
echo "文件上傳成功!<br/>path:".$name;
}else{
echo "文件不合法!";
}
}
?>
繞過(guò)方法:
* 0x00截?cái)嗬@過(guò)
* 此時(shí)若在iis6.0,則可以將木馬名改為test.asp;1.jpg來(lái)上傳,從而通過(guò)驗(yàn)證
* 配合解析漏洞
MIME驗(yàn)證
對(duì)文件MIME類型做驗(yàn)證的PHP代碼如下:
<?php
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_upload_file($imageTempName,"./uploadFile/".$imageName);
echo("文件上傳成功! path = /uploadFile/$imageName");
}else{
echo("文件上傳類型錯(cuò)誤,請(qǐng)重新上傳...");
exit();
}
?>
未修改MIME類型,上傳失敗:
修改MIME類型,上傳成功:
目錄驗(yàn)證
文件上傳時(shí)通常允許用戶將文件放到指定的目錄中,若目錄存在則將文件寫入目錄,否則新建目錄然后寫入,若為iis6.0則可以利用這個(gè)漏洞,客戶端上傳代碼如下:
<html>
<head>
<meta charset="UTF-8">
<title>up</title>
</head>
<body>
<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>
</body>
</html>
服務(wù)端PHP接收文件的代碼如下:
<?php
if($_FILES['file']['type']=="image/jpeg"){
$imageTempName=$_FILES['file']['tmp_name'];
$imageName=$_FILES['file']['name'];
$last=substr($imageName,strrpos($imageName,"."));
if($last!=".jpg"){
echo("mime error!<br/>");
}
$Extension=$_POST['Extension'];
if(!is_dir($Extension)){
mkdir("./$Extension");
echo "mkidr $Extension succesfully"."<br/>";
}
$imageName=md5($imageName).$last;
move_uploaded_file($imageTempName,"./$Extension/".$imageName);
echo("upload ok! path = /$Extension/$imageName");
} else {
echo("type error...");
exit();
}
?>
查看上傳到了那個(gè)文件:
將文件改名:
截?cái)嗌蟼鞴?/h4>
截?cái)嗌蟼鞴粼贏SP程序中比較常見(jiàn)(在PHP、JSP中也有)
先上傳正常后綴的圖片馬:
upload_vuln_upload_normal_picture.png
更改圖片名字:
upload_vuln_alter_picture_name.png
截?cái)啵?br>
upload_vuln_truncate_picture_name.png
上傳成功:
upload_vuln_upload_success.png
.htaccess文件攻擊
通過(guò).htaccess文件調(diào)用php解析器去解析一個(gè)文件名中只要包含"haha"這個(gè)字符串的任意文件,無(wú)論擴(kuò)展名是什么(沒(méi)有也行),都以php的方式來(lái)解析,.haccess文件代碼如下:
<FilesMatch "haha">
SetHandler application/x-httpd-php
</FilesMatch>
截?cái)嗌蟼鞴粼贏SP程序中比較常見(jiàn)(在PHP、JSP中也有)
先上傳正常后綴的圖片馬:
更改圖片名字:
截?cái)啵?br>
上傳成功:
通過(guò).htaccess文件調(diào)用php解析器去解析一個(gè)文件名中只要包含"haha"這個(gè)字符串的任意文件,無(wú)論擴(kuò)展名是什么(沒(méi)有也行),都以php的方式來(lái)解析,.haccess文件代碼如下:
<FilesMatch "haha">
SetHandler application/x-httpd-php
</FilesMatch>
或者如下,上傳一個(gè)文件名為evil.gif的圖片馬:
<FilesMatch "evil.gif">
SetHandler application/x-httpd-php
</FilesMatch>
檢測(cè)文件內(nèi)容
- 文件幻數(shù)檢測(cè)
在文件首部加上如下幻數(shù),后面跟一句話木馬即可
JFIF FF D8 FF E0 00 10 4A 46 49 46
GIF89a 47 49 46 38 39 61
PNG 89 50 4E 47
- 文件相關(guān)信息檢測(cè)
通常用的getimagesize()函數(shù),只需要在幻數(shù)基礎(chǔ)上加一些文件信息就行了,如下:
GIF89a
(...some binary data for image...)
<?php phpinfo(); ?>
(... skipping the rest of binary data ...)
- 文件加載檢測(cè)
服務(wù)端會(huì)調(diào)用API或函數(shù)對(duì)文件進(jìn)行加載測(cè)試,常見(jiàn)的是圖像渲染測(cè)試,變態(tài)的甚至是二次渲染:
對(duì)渲染/加載測(cè)試的攻擊方式是代碼注入繞過(guò)
對(duì)二次渲染的攻擊方式是攻擊文件加載器本身
補(bǔ)充
除了上述的.htaccess文件攻擊,還可以用.user.ini進(jìn)行文件攻擊
當(dāng)中間鍵是以fastcgi運(yùn)行的php都可以用這個(gè)方法,.user.ini能被動(dòng)態(tài)加載,它也有兩個(gè)配置項(xiàng):
auto_append_file和auto_prepend_file,只要在.user.ini中添加auto_prepend_file=aa.jpg
這句話,就可以讓其他php文件執(zhí)行前自動(dòng)包含aa.jpg,和require()類似。