2012-10-04 78 views
2

考慮下面的代碼可以繞過file_get_contents參數的附加文件後綴嗎?

<?php 
// warning: this code is unsafe and for demonstrational purposes only, 
// do not use in a production environment 
$filename = $_GET['filename']; 
$extension = 'txt'; 
$path = '/absolute/path'; 
$fullFilename = sprintf('%s/%s.%s', $path, $filename, $extension); 
echo file_get_contents($fullFilename); 

大家都知道(至少我希望如此),其前面加上了絕對路徑是沒有辦法充分的平均值,以防止離開給定的路徑 - 一個可以簡單地插入一個或多個「 ../「到查詢字符串以到達文件系統上的任何路徑。

我的問題是:類似於給定的示例,可以$_GET['filename']也可以以這樣的方式操縱該給定擴展後綴也可被旁路,即比其它的.txt文件是呼應?我特別想到某些控制字符繞過附加的文件擴展名,其方式與../用前綴相同。

我嘗試添加一些控制字符(例如ASCII碼127刪除)查詢字符串或使用&&|>,但都無濟於事連接兩個文件名,我想知道是否存在這樣的可能性,在所有。

(順便說一句,我想補充說明,這個問題是沒有要求的目的利用的系統。這純粹是在我的腦海來到最近一個假設性的問題。)

回答

2

肯定的是,nullbyte\0(或%00如果你想測試一個查詢字符串),將有file_get_contents()停止處理字符串時,它擊中它。

所以,如果$_GET['filename']../../../../../etc/passwd%00(如your_page.php?filename=../../../../etc/passwd%00),file_get_contents()將再也看不到結束.txt(它會看到它,但不會對其進行處理)。

不幸的是,sprintf在構建字符串時不會去除空字節。我不確定其他控制字符,但如果有的話 - 我總是建議去掉空字節。但是,更直觀的方法是構建允許字符的白名單並針對輸入執行preg_match()

對於這個問題,您可以在構建的字符串上使用PHP的realpath(),它將確定真正被訪問的完整路徑。 因此,/absolute/path/../../../../etc/passwd\0.txt,傳遞給realpath()將返回/etc/passwd。然後,您可以對返回的值進行進一步驗證(例如,擴展名仍然是.txt,或者它位於指定的/必需的目錄中)。

+0

感謝您的全面回答,我沒有想到nullbyte。然而,我試着用'$ filename =「testfile \ 0」'在指定路徑下放置一個文件,但是'file_get_contents'返回'false'(Ubuntu 11.04,PHP 5.3.5)。這個環境是否依賴? –

+0

@moeso不是我所知道的;我測試了你的代碼和'$ filename =「../../../../../ etc/passwd \ 0」;'的副本+粘貼並且它工作正常(打印出'/ etc/passwd ')。我找不到任何文件說明它的系統/版本相關 - 但總是有機會我錯過了某些課程。 – newfurniturey

+2

後續行動:只是爲了好玩,我嘗試了與我上面評論中描述的相同,唯一的區別是我使用了古老但剛剛編譯好的PHP版本4.4.49和5.2.0 - 以及您描述的nullbyte技巧,它們都適用於魅力。所以在5.2.0和5.3.5之間,似乎這個洞已經關閉了。 –

相關問題