學習筆記三:文件上傳漏洞
文件上傳漏洞類型
- 客戶端javascript校驗
- 服務端校驗
- content-type校驗(黑白名單校驗)
- 後綴名校驗(黑白名單校驗)
- 文件頭校驗(每種文件格式的開頭都有一段代碼指明其文件類型,通過修改後綴名繞不過這種檢驗)
- 自定義正則校驗
- WAF校驗
1.客戶端校驗
這個在測試時遇到了一個小問題,這裏建議大家不要用chrome瀏覽器,不知道是爲什麼,通過chrome始終上傳不了,換用firefox測試成功。其實javascript限制繞過特別簡單,因爲javascript代碼在前端,直接修改其代碼即可,將點擊校驗函數直接刪除即可。
2.服務端校驗
- 2.1 mime類型校驗
//只通過MIME類型驗證了一下圖片類型,其他的無驗證,upsafe_upload_check.php
function upload_sick($key,$mime,$save_path){
$arr_errors=array(
1=>'上傳的文件超過了 php.ini中 upload_max_filesize 選項限制的值',
2=>'上傳文件的大小超過了 HTML 表單中 MAX_FILE_SIZE 選項指定的值',
3=>'文件只有部分被上傳',
4=>'沒有文件被上傳',
6=>'找不到臨時文件夾',
7=>'文件寫入失敗'
);
if(!isset($_FILES[$key]['error'])){
$return_data['error']='請選擇上傳文件!';
$return_data['return']=false;
return $return_data;
}
if ($_FILES[$key]['error']!=0) {
$return_data['error']=$arr_errors[$_FILES[$key]['error']];
$return_data['return']=false;
return $return_data;
}
//驗證一下MIME類型
if(!in_array($_FILES[$key]['type'], $mime)){
$return_data['error']='上傳的圖片只能是jpg,jpeg,png格式的!';
$return_data['return']=false;
return $return_data;
}
//新建一個保存文件的目錄
if(!file_exists($save_path)){
if(!mkdir($save_path,0777,true)){
$return_data['error']='上傳文件保存目錄創建失敗,請檢查權限!';
$return_data['return']=false;
return $return_data;
}
}
$save_path=rtrim($save_path,'/').'/';//給路徑加個斜槓
if(!move_uploaded_file($_FILES[$key]['tmp_name'],$save_path.$_FILES[$key]['name'])){
$return_data['error']='臨時文件移動失敗,請檢查權限!';
$return_data['return']=false;
return $return_data;
}
//如果以上都通過了,則返回這些值,存儲的路徑,新的文件名(不要暴露出去)
$return_data['new_path']=$save_path.$_FILES[$key]['name'];
$return_data['return']=true;
return $return_data;
}
上面是校驗函數,可以看出是基於白名單的mime類型校驗。這裏只需要抓一下請求包,將mime改成白名單的內容即可。
- 2.2文件頭校驗
常見的一些文件類型的文件頭,簡單一點來說就是文件的前導碼。
- (1).JPEG;.JPE;.JPG,”JPGGraphic File”
- (2).gif,”GIF 89A”
- (3).zip,”Zip Compressed”
- (4).doc;.xls;.xlt;.ppt;.apr,”MSCompound Document v1 or Lotus Approach APRfile”
只要將這些前導碼加到文件的開頭就可以繞過文件頭檢測。
- 2.3文件後綴名檢測
這個在windows php5上測試是不成功的,因爲後臺是基於白名單的驗證,所以直接上後綴名階段,但最終上傳到服務器上的始終是jpg。看了一下他的檢測函數,發現,
//進行了嚴格的驗證
function upload($key,$size,$type=array(),$mime=array(),$save_path){
$arr_errors=array(
1=>'上傳的文件超過了 php.ini中 upload_max_filesize 選項限制的值',
2=>'上傳文件的大小超過了 HTML 表單中 MAX_FILE_SIZE 選項指定的值',
3=>'文件只有部分被上傳',
4=>'沒有文件被上傳',
6=>'找不到臨時文件夾',
7=>'文件寫入失敗'
);
// var_dump($_FILES);
if(!isset($_FILES[$key]['error'])){
$return_data['error']='請選擇上傳文件!';
$return_data['return']=false;
return $return_data;
}
if ($_FILES[$key]['error']!=0) {
$return_data['error']=$arr_errors[$_FILES[$key]['error']];
$return_data['return']=false;
return $return_data;
}
//驗證上傳方式
if(!is_uploaded_file($_FILES[$key]['tmp_name'])){
$return_data['error']='您上傳的文件不是通過 HTTP POST方式上傳的!';
$return_data['return']=false;
return $return_data;
}
//獲取後綴名,如果不存在後綴名,則將變量設置爲空
$arr_filename=pathinfo($_FILES[$key]['name']);
if(!isset($arr_filename['extension'])){
$arr_filename['extension']='';
}
//先驗證後綴名
if(!in_array(strtolower($arr_filename['extension']),$type)){//轉換成小寫,在比較
$return_data['error']='上傳文件的後綴名不能爲空,且必須是'.implode(',',$type).'中的一個';
$return_data['return']=false;
return $return_data;
}
//驗證MIME類型,MIME類型可以被繞過
if(!in_array($_FILES[$key]['type'], $mime)){
$return_data['error']='你上傳的是個假圖片,不要欺騙我xxx!';
$return_data['return']=false;
return $return_data;
}
//通過getimagesize來讀取圖片的屬性,從而判斷是不是真實的圖片,還是可以被繞過的
if(!getimagesize($_FILES[$key]['tmp_name'])){
$return_data['error']='你上傳的是個假圖片,不要欺騙我!';
$return_data['return']=false;
return $return_data;
}
//驗證大小
if($_FILES[$key]['size']>$size){
$return_data['error']='上傳文件的大小不能超過'.$size.'byte(500kb)';
$return_data['return']=false;
return $return_data;
}
//把上傳的文件給他搞一個新的路徑存起來
if(!file_exists($save_path)){
if(!mkdir($save_path,0777,true)){
$return_data['error']='上傳文件保存目錄創建失敗,請檢查權限!';
$return_data['return']=false;
return $return_data;
}
}
//生成一個新的文件名,並將新的文件名和之前獲取的擴展名合起來,形成文件名稱
$new_filename=str_replace('.','',uniqid(mt_rand(100000,999999),true));
if($arr_filename['extension']!=''){
$arr_filename['extension']=strtolower($arr_filename['extension']);//小寫保存
$new_filename.=".{$arr_filename['extension']}";
}
//將tmp目錄裏面的文件拷貝到指定目錄下並使用新的名稱
$save_path=rtrim($save_path,'/').'/';
if(!move_uploaded_file($_FILES[$key]['tmp_name'],$save_path.$new_filename)){
$return_data['error']='臨時文件移動失敗,請檢查權限!';
$return_data['return']=false;
return $return_data;
}
//如果以上都通過了,則返回這些值,存儲的路徑,新的文件名(不要暴露出去)
$return_data['save_path']=$save_path.$new_filename;
$return_data['filename']=$new_filename;
$return_data['return']=true;
return $return_data;
}
?>
從上面的函數可以看出,該方法對上傳的文件進行了重命名,又是基於白名單的後綴名,所以這次繞過是失敗了的。暫時也沒有想到如何繞過這種上傳檢測。
-
基於黑名單的上傳繞過方法
-
改變後綴大小寫
-
使用特殊後綴名 asa , cer
-
截斷繞過:
test.php(0x00).jpg
test.php%00.jpg
-
-
常見的能被解析的後綴名
jsp,jspx,jspf
asp,asa,cer,aspx
php php3 php4
exe exee
至於這一塊做免殺的話,可以使用文件包含的方法,先上傳一個普通文件,再上傳一個包含這個文件的腳本文件。從而實現免殺
-
2.4CMS,編輯器漏洞繞過
- 針對不同cms的漏洞進行上傳
- 編輯器漏洞,通過一些編輯器出現的漏洞實現檢測繞過上傳
-
2.5補充
配合操作系統文件命令規則
- (1)上傳不符合windows文件命名規則的文件名
test.asp.
test.asp(空格)
test.php:1.jpg
test.php::$
DATA
shell.php::$
DATA…….
會被windows系統自動去掉不符合規則符號後面的內容。 - (2)linux下後綴名大小寫
在linux下,如果上傳php不被解析,可以試試上傳pHp後綴的文件名。
3.WAF繞過
-
1.添加干擾信息,在請求保重添加無關參數,干擾waf檢測。(不過現在感覺這種方法很雞肋,阿里雲盾處理能力還是挺強的。)
-
2.修改上傳目錄,看到一個視頻上說,如過文件夾名是a.php下面所有的文件都被當作php文件解析。
-
3.更改請求方式 POST/GET
-
4.刪除實體裏面的Conten-Type字段
-
5.其它
特殊的長文件名繞過文件名使用非字母數字,比如中文等最大程度的拉長,不行的話再結合一下其他的特性進行測試:
shell.asp;王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王.jpg