作者:Anthem
來源:信安之路(ID:xazlsec)
前言
文件上傳漏洞可以說是日常滲透測試用得最多的一個漏洞,因為用它獲得服務器權限最快最直接。但是想真正把這個漏洞利用好卻不那麼容易,其中有很多技巧,也有很多需要掌握的知識。俗話說,知己知彼方能百戰不殆,因此想要研究怎麼防護漏洞,就要了解怎麼去利用。
特點
- 利用簡單
- 危害大
產生原因
缺少必要的校驗
代碼審計
基礎
關於PHP中$_FILES數組的使用方法
$_FILES/[‘file’][‘name’] 客戶端文件名稱 $_FILES/[‘file’][‘type’] 文件的MIME類型 $_FILES/[‘file’][‘size’] 文件大小 單位字節 $_FILES/[‘file’][‘tmp_name’] 文件被上傳后再服務器端臨時文件名,可以在php.ini中指定
需要注意的是在文件上傳結束后,默認的被儲存在臨時文件夾中,這時必須把他從臨時目錄中刪除或移動到其他地方,否則,腳本運行完畢后,自動刪除臨時文件,可以使用copy或者*move_uploaded_file
兩個函數
程序員對某些常用函數的錯誤認識
這些函數有:empty()、isset()、strpos()、rename()
等,如下面的代碼:
#!php if($operateId == 1){ $date = date("Ymd"); $dest = $CONFIG->basePath."data/files/".$date."/"; $COMMON->createDir($dest); //if (!is_dir($dest)) mkdir($dest, 0777); $nameExt = strtolower($COMMON->getFileExtName($_FILES['Filedata']['name'])); $allowedType = array('jpg', 'gif', 'bmp', 'png', 'jpeg'); if(!in_array($nameExt, $allowedType)){ $msg = 0; } if(empty($msg)){ $filename = getmicrotime().'.'.$nameExt; $file_url = urlencode($CONFIG->baseUrl.'data/files/'.$date."/".$filename); $filename = $dest.$filename; if(empty($_FILES['Filedata']['error'])){ move_uploaded_file($_FILES['Filedata']['tmp_name'],$filename); } if (file_exists($filename)){ //$msg = 1; $msg = $file_url; @chmod($filename, 0444); }else{ $msg = 0; } } $outMsg = "fileUrl=".$msg; $_SESSION["eoutmsg"] = $outMsg; exit; }
我們來看上面的這段代碼,要想文件成功的上傳, if(empty($msg)) 必須為True才能進入if的分支,接下來我們來看empty函數何時返回True,看看PHP Manual怎麼說,如圖
客戶端校驗
JavaScript校驗
驗證代碼
<?php //文件上傳漏洞演示腳本之js驗證 $uploaddir = 'uploads/'; if (isset($_POST['submit'])) { if (file_exists($uploaddir)) { if (move_uploaded_file($_FILES['upfile']['tmp_name'], $uploaddir . '/' . $_FILES['upfile']['name'])) { echo '文件上傳成功,保存於:' . $uploaddir . $_FILES['upfile']['name'] . "/n"; } } else { exit($uploaddir . '文件夾不存在,請手工創建!'); } //print_r($_FILES); } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html;charset=gbk"/> <meta http-equiv="content-language" content="zh-CN"/> <title>文件上傳漏洞演示腳本--JS驗證實例</title> <script type="text/javascript"> function checkFile() { var file = document.getElementsByName('upfile')[0].value; if (file == null || file == "") { alert("你還沒有選擇任何文件,不能上傳!"); return false; } //定義允許上傳的文件類型 var allow_ext = ".jpg|.jpeg|.png|.gif|.bmp|"; //提取上傳文件的類型 var ext_name = file.substring(file.lastIndexOf(".")); //alert(ext_name); //alert(ext_name + "|"); //判斷上傳文件類型是否允許上傳 if (allow_ext.indexOf(ext_name + "|") == -1) { var errMsg = "該文件不允許上傳,請上傳" + allow_ext + "類型的文件,當前文件類型為:" + ext_name; alert(errMsg); return false; } } </script> <body> <h3>文件上傳漏洞演示腳本--JS驗證實例</h3> <form action="" method="post" enctype="multipart/form-data" name="upload" onsubmit="return checkFile()"> <input type="hidden" name="MAX_FILE_SIZE" value="204800"/> 請選擇要上傳的文件:<input type="file" name="upfile"/> <input type="submit" name="submit" value="上傳"/> </form> </body> </html>
客戶端JS驗證通常做法是驗證上傳文件的擴展名是否符合驗證條件
繞過姿勢
1.通過firefox的F12修改js代碼繞過驗證
2.使用burp抓包直接提交,繞過js驗證
服務器端校驗
文件頭content-type字段校驗(服務端MIME類型檢測)
MIME類型介紹
MIME type的縮寫為(Multipurpose Internet Mail Extensions)代表互聯網媒體類型(Internet media type),MIME使用一個簡單的字符串組成,最初是為了標識郵件Email附件的類型,在html文件中可以使用content-type屬性表示,描述了文件類型的互聯網標準。
Internet中有一個專門組織IANA來確認標準的MIME類型,但Internet發展的太快,很多應用程序等不及IANA來確認他們使用的MIME類型為標準類型。因此他們使用在類別中以x-開頭的方法標識這個類別還沒有成為標準,例如:x-gzip,x-tar等。事實上這些類型運用的很廣泛,已經成為了事實標準。只要客戶機和服務器共同承認這個MIME類型,即使它是不標準的類型也沒有關係,客戶程序就能根據MIME類型,採用具體的處理手段來處理數據。
Response對象通過設置ContentType使客戶端瀏覽器,區分不同種類的數據,並根據不同的MIME調用瀏覽器內不同的程序嵌入模塊來處理相應的數據。
MIME類型格式:
類別/子類別;參數
Content-Type: [type]/[subtype]; parameter
MIME主類別:
text:用於標準化地表示的文本信息,文本消息可以是多種字符集和或者多種格式的;
Multipart:用於連接消息體的多個部分構成一個消息,這些部分可以是不同類型的數據;
Application:用於傳輸應用程序數據或者二進制數據;
Message:用於包裝一個E-mail消息;
Image:用於傳輸靜態圖片數據;
Audio:用於傳輸音頻或者音聲數據;
Video:用於傳輸動態影像數據,可以是與音頻編輯在一起的視頻數據格式。
常見MIME類型:
利用PHP特性(使用數組繞過)
转载请注明:IAMCOOL » 簡單粗暴的文件上傳漏洞