50. 檔案上傳篇——檔案上傳漏洞原理
檔案上傳漏洞
檔案上傳漏洞是WEB安全中經常利用到的一種漏洞形式。這種型別的攻擊從大的型別上來說是攻擊“資料與程式碼分理原則”的一種攻擊。
一些web應用程式中允許上傳圖片,文字或者其他資源到指定的位置,檔案上傳漏洞就是利用這些可以上傳的地方將惡意程式碼植入到伺服器中,再通過url去訪問以執行程式碼
造成檔案上傳漏洞的原因
(1)對於上傳檔案的字尾名(副檔名)沒有做較為嚴格的限制
(2)對於上傳檔案的MIMETYPE 沒有做檢查
(3)許可權上沒有對於上傳的檔案的檔案許可權,(尤其是對於shebang型別的檔案)
(4)對於web server對於上傳檔案或者指定目錄的行為沒有做限制
檔案上傳漏洞防禦方式與繞過方式
1.前端限制
function check(){ var filename=document.getElementById("file"); var str=filename.value.split("."); var ext=str[str.length-1]; if(ext=='jpg'||ext=='png'||ext=='jpeg'||ext=='gif'){ return true; }else{ alert("這不是圖片!") return false; } return false; }
在表單中使用onsumbit=check()呼叫js函式來檢查上傳檔案的副檔名。這種限制實際上沒有任何用處,任何攻擊者都可以輕而易舉的破解。只能用於對於使用者完全信任的情況下,很難稱之為一種安全措施只能稱之是一種防止使用者誤操作上傳的措施,
反制:
隨便的編輯一下頁面/用burpsuite/寫個小指令碼就可以突破之,無須多言
2.檢查副檔名
顧名思義,就是在檔案被上傳到服務端的時候,對於檔名的副檔名進行檢查,如果不合法,則拒絕這次上傳
在這裡,還有一點是值得一提的,在檢查副檔名是否合法的時候,有兩種策略
黑名單策略,副檔名在黑名單中的為不合法,示例程式碼
$postfix = end(explode('.','$_POST['filename']); if($postfix=='php'||$postfix=='asp'||$postfix=='sh'){ echo "invalid file type"; return; }
白名單策略,副檔名不在白名單中的均為不合法
$postfix = end(explode('.','$_POST['filename']);
if($postfix=='jpg'||$postfix=='png'||$postfix=='gif'){
//save the file and do something next
} else {
echo "invalid file type";
return;
}
白名單策略是更加安全的,通過限制上傳型別為只有我們接受的型別,可以較好的保證安全,因為黑名單我們可以使用各種方法來進行注入和突破
反制
在一些 webserver 中,存在解析漏洞
1.老版本的IIS中的目錄解析漏洞,如果網站目錄中有一個 /.asp/目錄,那麼此目錄下面的一切內容都會被當作asp指令碼來解析
2.老闆本的IIS中的分號漏洞:IIS在解析檔名的時候可能將分號後面的內容丟棄,那麼我們可以在上傳的時候給後面加入分號內容來避免黑名單過濾,如 a.asp;jpg
3.舊版Windows Server中存在空格和dot漏洞類似於 a.php. 和 a.php[空格] 這樣的檔名儲存後會被windows去掉點和空格,從而使得加上這兩個東西可以突破過濾,成功上傳,並且被當作php程式碼來執行
4.nginx空位元組漏洞 xxx.jpg%00.php 這樣的檔名會被解析為php程式碼執行
5.apache的解析漏洞,上傳如a.php.rar a.php.gif 型別的檔名,可以避免對於php檔案的過濾機制,但是由於apache在解析檔名的時候是從右向左讀,如果遇到不能識別的副檔名則跳過,rar等副檔名是apache不能識別的,因此就會直接將型別識別為php,從而達到了注入php程式碼的目的
3.檢查HTTP Header中的Content-Type
HTTP協議規定了上傳資源的時候在Header中加上一項檔案的MIMETYPE,來識別檔案型別,這個動作是由瀏覽器完成的,服務端可以檢查此型別不過這仍然是不安全的,因為HTTP header可以被髮出者或者中間人任意的修改,不過加上一層防護也是可以有一定效果的
反制
使用各種各樣的工具(如burpsuite)強行篡改Header就可以,太容易將header中的
Content-Type: application/php
或者其他型別
改為
Content-Type: image/jpg
Content-Type: image/png
Content-Type: text/plain
等這些web程式允許的淚洗改附上常用的MIMETYPE表
text/plain(純文字)
text/html(HTML文件)
text/javascript(js程式碼)
application/xhtml+xml(XHTML文件)
image/gif(GIF影象)
image/jpeg(JPEG影象)
image/png(PNG影象)
video/mpeg(MPEG動畫)
application/octet-stream(二進位制資料)
application/pdf(PDF文件)
application/(程式語言) 該種語言的程式碼
application/msword(Microsoft Word檔案)
message/rfc822(RFC 822形式)
multipart/alternative(HTML郵件的HTML形式和純文字形式,相同內容使用不同形式表示)
application/x-www-form-urlencoded(POST方法提交的表單)
multipart/form-data(POST提交時伴隨檔案上傳的表單)
4.分析檔案頭內容來檢查檔案型別
與方法2不同,還有一種檢查型別的方式是使用對於檔案內容的驗證機制,這種方法利用的是每一個特定型別的檔案都會有不太一樣的開頭或者標誌位。可以通過比如php的exif_imagetype()函式,一個通過這種方法來過濾的示例程式碼如下:
if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
echo "File is not an image";
return;
}
也可以自己編寫函式來進行識別,圖片檔案通常有稱作幻數的頭位元組,我們來看一下幾種圖片檔案的幻數:
(注意!下面是二進位制而不是文字格式的資料)
JPG
FF D8 FF E0 00 10 4A 46 49 46
GIF
47 49 46 38 39 61
(相當於文字的GIF89a)
PNG
89 50 4E 47
通過檢查頭幾位位元組,可以分辨是否是圖片檔案
如果是其他型別的二進位制檔案,也有響應的頭位元組,如下表
反制
給上傳指令碼加上相應的幻數頭位元組就可以,php引擎會將 <?之前的內容當作html文字,不解釋而跳過之,後面的程式碼仍然能夠得到執行比如下面:
(一般不限制圖片檔案格式的時候使用GIF的頭比較方便,因為全都是文字可列印字元。)
GIF89a
<?php
do_something();
?>
如果是其他型別的二進位制檔案,也有響應的頭位元組,如下表
格式 | 檔案頭 |
---|---|
TIFF (tif) | 49492A00 |
Windows Bitmap (bmp) | 424D |
CAD (dwg) | 41433130 |
Adobe Photoshop (psd) | 38425053 |
Rich Text Format (rtf) | 7B5C727466 |
MS Word/Excel (xls.or.doc) | D0CF11E0 |
MS Access (mdb) | 5374616E64617264204A |
ZIP Archive (zip), | 504B0304 |
RAR Archive (rar), | 52617221 |
Wave (wav), | 57415645 |
AVI (avi), | 41564920 |
Real Media (rm), | 2E524D46 |
MPEG (mpg), | 000001BA |
MPEG (mpg), | 000001B3 |
Quicktime (mov), | 6D6F6F76 |
Adobe Acrobat (pdf), | 255044462D312E |
Windows Media (asf), | 3026B2758E66CF11 |
MIDI (mid), | 4D546864 |
5.限制Web Server對於特定型別檔案的行為
導致檔案上傳漏洞的根本原因在於服務把使用者上傳的本應是資料的內容當作了程式碼,一般來說,使用者上傳的內容都會被儲存到特定的一個資料夾下,比如我們很多人習慣於放在 ./upload/ 下面要防止資料被當作程式碼執行,我們可以限制web server對於特定資料夾的行為。
大多數服務端軟體都可以支援使用者對於特定型別檔案的行為的自定義,以Apache為例:
在預設情況下,對與 .php檔案Apache會當作程式碼來執行,對於 html,css,js檔案,則會直接由HTTP Response交給客戶端程式對於一些資原始檔,比如txt,doc,rar等等,則也會以檔案下載的方式傳送的客戶端。我們希望使用者上傳的東西僅僅當作資源和資料而不能當作程式碼
因此可以使用伺服器程式的介面來進行限制
以Apache為例,我們可以利用 .htaccess 檔案機制來對web server行為進行限制
在這裡插一句,如果不是專門的檔案下載目錄,請務必關掉資料夾瀏覽的許可權,以防止嗅探和可能的越權,也是使用.htaccess檔案,在其中加上一句
Options All -Indexes
即可。
禁止指令碼執行有多種方式可以實現,而且分別有不同的效果,我們分別來看一下
1.指定特定副檔名的檔案的處理方式,原理是指定Response的Content-Type可以加上如下幾行
AddType text/plain .pl .py .php
這種情況下,以上幾種指令碼檔案會被當作純文字來顯示出來,你也可以換成其他的Content-Type
2.如果要完全禁止特定副檔名的檔案被訪問,用下面的幾行
Options -ExecCGI
AddHandler cgi-script .php .pl .py .jsp .asp .htm .shtml .sh .cgi識別
在這種情況下,以上幾種型別的檔案被訪問的時候,會返回403 Forbidden的錯誤
3.也可以強制web伺服器對於特定檔案型別的處理,與第一條不同的是, 下面的方法直接強行讓apache將檔案識別為你指定的型別,而第一種是讓瀏覽器
<FilesMatch "\.(php|pl|py|jsp|asp|htm|shtml|sh|cgi)$">
ForceType text/plain
</FilesMatch>
看程式碼就可以很明白的知道,符合上面正則的全部被認為是純文字,也可以繼續往裡面加入其他型別。
4.只允許訪問特定型別的檔案
<Files ^(*.jpeg|*.jpg|*.png|*.gif)>
order deny,allow
deny from all
</Files>
在一個上傳圖片的資料夾下面,就可以加上這段程式碼,使得該資料夾裡面只有圖片副檔名的檔案才可以被訪問,其他型別都是拒絕訪問。
這又是一個白名單的處理方案
永遠記得,白名單是最有保障的安全措施
反制
可以通過 move_uploaded_file 函式把自己寫的.htaccess 檔案上傳,覆蓋掉伺服器上的檔案,來定義檔案型別和執行許可權如果做到了這一點,將獲得相當大的許可權。
總結安全性檢查如下
檔名——目錄穿越、SQL注入、解析漏洞
檔案大小——zip 拒絕服務
檔案內容——圖片馬;xls批量上傳相關可能會導致xss、sql注入等
檔案型別——自由發揮的空間很大,繞過姿勢除文中所說之外,還有檔案包含、雙檔案上傳、資料二意性、後臺修改等。