標籤:驀然回首 目錄 程式 style 無法 證明 test 郵件系統 tac
早上逛烏雲發現了PKAV大牛的一篇文章,針對php和windows檔案上傳的分析,思路很YD,果斷轉之與大家分享。
雖然此文可能有許多的限制條件,但是如果你認真閱讀會發現,其實還是比較實用的。
另外一篇團長發的pdf中也涉及到了相關的文章,是國外的nosec發布的,英文的,感興趣的同學在這裡下載http://pan.baidu.com/s/1hqiQRbA
本案例採用的執行個體是:U-Mail郵件系統。
U-Mail郵件系統檔案上傳的地方代碼是這樣的:
<?phpif(ACTION =="attach-upload"){ if($_FILES){ $file_name = $_FILES[‘Filedata‘][‘name‘]; $file_type = $_FILES[‘Filedata‘][‘type‘]; $file_size = $_FILES[‘Filedata‘][‘size‘]; $file_source = $_FILES[‘Filedata‘][‘tmp_name‘]; $file_suffix = getfilenamesuffix( $file_name ); $not_allow_ext = array( "php", "phps", "php3", "exe", "bat" ); if (in_array($file_suffix, $not_allow_ext )){ dump_json( array( "status" => 0, "message" => el( "不支援該副檔名檔案上傳", "" ) ) ); } $path_target = getusercachepath( ); do{ $file_id = makerandomname( ); $file_target = $path_target.$file_id.".".$file_suffix; } while ( file_exists( $file_target ) ); if ( move_uploaded_file( $file_source, $file_target )){ dump_json( array( "status" => 0, "message" => el( "寫入檔案出錯,請與管理員聯絡!", "" ) ) ); } $_SESSION[SESSION_ID][‘attach_cache‘][] = array( "id" => $file_id, "name" => $file_name, "type" => "1", "path" => $file_target, "size" => $file_size ); dump_json( array( "status" => "1", "filename" => $file_name, "filesize" => $file_size, "file_id" => $file_id ) ); }else{ dump_json( array( "status" => "0", "message" => el( "無法找到需要上傳的檔案!", "" ) ) ); }}
我們注意到如下的代碼
$not_allow_ext = array( "php", "phps", "php3", "exe", "bat" );if (in_array($file_suffix, $not_allow_ext )){ dump_json( array( "status" => 0, "message" => el( "不支援該副檔名檔案上傳", "" ) ) );}
非常明顯,採用的是黑名單驗證,雖然我們可以採用類似這樣的檔案尾碼繞過程式的檢測,如:bypass.phpX(這裡的X代表空格%20或其他特殊字元{%80-%99}),但這完全不是今天我想要講的內容。
今天,通過這個執行個體給大家講解一種新型的檔案上傳方式,且聽我細細道來..
#2 代碼poc實現
為了在本地測試方便,我們對上述代碼進行簡化,如下
<?php//U-Mail demo ...if(isset($_POST[‘submit‘])){ $filename = $_POST[‘filename‘]; $filename = preg_replace("/[^\w]/i", "", $filename); $upfile = $_FILES[‘file‘][‘name‘]; $upfile = str_replace(‘;‘,"",$upfile); $upfile = preg_replace("/[^(\w|\:|\$|\.|\<|\>)]/i", "", $upfile); $tempfile = $_FILES[‘file‘][‘tmp_name‘]; $ext = trim(get_extension($upfile)); // null if(in_array($ext,array(‘php‘,‘php3‘,‘php5‘))){ die(‘Warning ! File type error..‘); } if($ext == ‘asp‘ or $ext == ‘asa‘ or $ext == ‘cer‘ or $ext == ‘cdx‘ or $ext == ‘aspx‘ or $ext == ‘htaccess‘) $ext = ‘file‘; //$savefile = ‘upload/‘.$upfile; $savefile = ‘upload/‘.$filename.".".$ext; if(move_uploaded_file($tempfile,$savefile)){ die(‘Success upload..path :‘.$savefile); }else{ die(‘Upload failed..‘); }}function get_extension($file){ return strtolower(substr($file, strrpos($file, ‘.‘)+1));}?><html> <body> <form method="post" action="upfile.php" enctype="multipart/form-data"> <input type="file" name="file" value=""/> <input type="hidden" name="filename" value="file"/> <input type="submit" name="submit" value="upload"/> </form> </body></html>
對於上述代碼,雖然是通過黑名單進行檔案名稱檢測,但通過目前已知的上傳方法,是沒有辦法成功上傳php檔案的(不考慮程式的Bug),因此可以說這段檔案上傳的代碼是"安全"的,
可是,我驀然回首,在那個燈火闌珊的地方,php邂逅了Windows,美麗的愛情故事便由此產生了..
#3 細說故事
某天,二哥在群裡丟了一個url串連,我簡單看了下,關於利用系統特性進行檔案上傳的,興趣馬上就來了,就細細研究了下,於是有了這篇文章..
這幾行英文的意思大致是,在php+window+iis環境下:
雙引號(">") <==> 點號(".")‘;大於符號(">") <==> 問號("?")‘;小於符號("<") <==> 星號("*")‘;
有這麼好玩的東西,那不就可以做太多的事了?但事實並不是這樣,通過一系列的測試發現,該特性只能用於檔案上傳時覆蓋已知的檔案,於是這個特性便略顯雞肋..
原因有二:
1)上傳檔案的目錄一般我們都不可控;
2)同時,一般檔案上傳的目錄不可能存在我們想要的任何php檔案,因此沒辦法覆蓋;
後來,經過反反覆複的思考,終於找到了可以完美利用的辦法..
思路如下:
首先我們先利用特殊辦法產生一個php檔案,然後再利用這個特性將檔案覆蓋..
可問題又來了,怎樣產生php檔案呢?如果可以直接產生php檔案的話,幹嘛還要利用那什麼特性?
別急,辦法總是有的..
我們都知道在檔案上傳時,我們往往會考慮到檔案名稱截斷,如%00 等..
對!有的人可能還會用冒號(":")去截斷,如:bypass.php:jpg
但是你知道嗎?冒號截斷產生的檔案是空白的,裡面並不會有任何的內容,呵呵 說到這裡 明白了沒有? 雖然產生的php檔案裡面沒有內容,但是php檔案總產生了吧,所以 我們可以結合上面所說的特性完美成功利用..
#4 冒號+特性成功利用
按照#3提供的思路,實現..
本地測試地址:http://www.secmap.cn/upfile.php 環境:Windows+IIS7.5
1)首先利用冒號產生我們將要覆蓋的php檔案,這裡為:bypass.php,
2)利用上面的系統特性覆蓋該檔案
從上面已經知道"<" 就等於 "*",而"*"代碼任一字元,於是乎.. 我們可以這樣修改上傳的檔案名稱,如下:
------WebKitFormBoundaryaaRARrn2LBvpvcwKContent-Disposition: form-data; name="file"; filename="bypass.<<<"Content-Type: image/jpeg//注意!檔案名稱為:bypass.<<<
點擊go..,即可成功覆蓋bypass.php檔案,
對比上面的兩個圖,bypass.php被我們成功的寫入了內容..
#5 特性二
首先來看看微軟MSDN上面的一段話,
注意紅色圈起來的英文
The default data stream has no name. That is, the fully qualified name for the default stream for a file called "sample.txt" is "sample.txt::$DATA" since "sample.txt" is the name of the file and "$DATA" is the stream type.
看不去不錯喲,試試吧..
同樣,我們可以這樣修改上傳的檔案名稱,如下:
------WebKitFormBoundaryaaRARrn2LBvpvcwKContent-Disposition: form-data; name="file"; filename=‘DataStreamTest.php::$DATA‘Content-Type: image/jpeg//注意!檔案名稱為:DataStreamTest.php::$DATA
點擊GO,奇蹟出現了..
訪問之...
#6 漏洞證明
U-Mail,具體利用方法,同上述的方法一樣,為了簡單快捷的話,可直接抓包修改檔案名稱為:
shell.php::$DATA 即可成功上傳,這裡不再示範,附shell
當php邂逅windows通用上傳缺陷