在php中open_basedir是php中一個用得不多的函數,但是open_basedir函數一不小心就給人家給進入你服務器了,open_basedir到底有多神奇我們來看看吧。
先看一段我們不考慮open_basedir安全問題代碼
在php寫了句require_once ‘../Zend/Loader.php’; 報錯:
Warning: require_once() [function.require-once]: open_basedir restriction in effect. File(../Zend/Loader.php) is not within the allowed path(s): (D:/phpnow/vhosts/zf.com;C:/Windows/Temp;) in D:/phpnow/vhosts/zf.com/index.php on line 6
Warning: require_once(../Zend/Loader.php) [function.require-once]: failed to open stream: Operation not permitted in D:/phpnow/vhosts/zf.com/index.php on line 6
Fatal error: require_once() [function.require]: Failed opening required '../Zend/Loader.php' (include_path='D:/phpnow/vhosts/zf.comZend;.;C:/php5/pear') in D:/phpnow/vhosts/zf.com/index.php on line 6字面分析是受到了open_basedir的限制,造成Operation not permitted(操作不被允許)。
打開php.ini跳轉到open_basedir相關設置段落:
; open_basedir, if set, limits all file operations to the defined directory
; and below. This directive makes most sense if used in a per-directory
; or per-virtualhost web server configuration file. This directive is
; *NOT* affected by whether Safe Mode is turned On or Off.
;open_basedir =如果設置了open_basedir,那麼所有能被操作的文件就只能限制在open_basedir指定的目錄裡面。 這個在虛擬主機裡面這個指令相當有用。不管安全模式是否打開,這個指令都不受影響。 看來php.ini沒有設置open_basedir。 打開apache虛擬主機配置文件:
代碼如下 復制代碼<virtualhost *>
<directory "../vhosts/zf.com">
Options -Indexes FollowSymLinks
</directory>
ServerAdmin [email protected]
DocumentRoot "../vhosts/zf.com"
ServerName zf.com:80
ServerAlias *.zf.com
ErrorLog logs/zf.com-error_log
php_admin_value open_basedir "D:/phpnow/vhosts/zf.com;C:/Windows/Temp;"
</virtualhost>
裡面的php_admin_value open_basedir就限定了操作目錄。我這裡是本地測試,安全因素不考慮,直接將 php_admin_value open_basedir “D:/phpnow/vhosts/zf.com;C:/Windows/Temp;” 刪除掉,重新啟動apache,
上面如果給利用完可以可隨意刪除服務器文件了,但是比較幸運的是目前php站點的安全配置基本是open_basedir+safemode,確實很無敵、很安全,即使在權限沒有很好設置的環境中,這樣配置都是相當安全的,當然了,不考慮某些可以繞過的情況。本文討論兩點開啟open_basedir後可能導致的安全隱患(現實遇到的),一個也許屬於php的一個小bug,另外一個可能是由於配置不當產生的。
一、open_basedir中處理文件路徑時沒有嚴格考慮目錄的存在,這將導致本地包含或者本地文件讀取的繞過。
看一個本地文件任意讀取的例子:
代碼如下 復制代碼 <?php雖然file是任意提交的,但是限制了前綴必須為img,我們如果想跳出目錄讀文件,比如,讀取網站根目錄下的config.php,我們得提交?file=img/../../config.php,但是此處有個限制條件,就是upload目錄下不存在img文件夾,在windows文件系統裡,系統不會去考慮目錄存在不存在,會直接跳轉目錄從而導致漏洞;但linux文件系統非常嚴謹,它會仔細判斷每一層目錄是否存在,比如這裡由於不存在img,則跳出去讀取文件的時候直接報錯。看如下一個示意圖:
再看一個類似的本地包含的例子:
由於linux文件系統的限制,我們無法利用旁注去包含tmp下的文件。
linux嚴謹的考慮在php那裡顯然沒有得到深刻的體會。在開啟了open_basedir的時候,php對傳入的文件路徑進行了取真實路徑的處理,然後跟open_basedir中設置的路徑進行比較:
代碼如下 復制代碼……
/* normalize and expand path */
if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
return -1;
}
path_len = strlen(resolved_name);
memcpy(path_tmp, resolved_name, path_len + 1); /* safe */
……
但php在處理的時候忽略了檢查路徑是否存在,於是在開啟了open_basedir時,上面那個文件讀取的例子,我們可以使用?file=img/../../config.php來直接讀取了,此時提交的路徑已經被處理成/home/www/config.php了,所以不存在任何讀取問題了。
問題由滲透測試的時候遇到繞過的情況從而導致疑問,經分析環境差異,然後xi4oyu牛指點有可能是open_basedir的問題後測試總結出這是php的一個小的bug,但是很有可能導致安全隱患。
二、open_basedir的值配置不當,有可能導致目錄跨越。
很多管理員都知道設置open_basedir,但在配置不當的時候可能發生目錄跨越的問題。
錯誤的配置:/tmp:/home/www,正確的配置:/tmp/:/home/www/
代碼如下 復制代碼……
/* Resolve open_basedir to resolved_basedir */
if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) {
/* Handler for basedirs that end with a / */
resolved_basedir_len = strlen(resolved_basedir);
if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) {
if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR;
resolved_basedir[++resolved_basedir_len] = '/0';
}
} else {
resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR;
resolved_basedir[resolved_basedir_len] = '/0';
}
……
php考慮了以/結束的路徑,但是如果沒有/,就直接帶入下文比較了。
於是,當新建一個網站為 /home/wwwoldjun/(均已經分別設置open_basedir),如果配置錯誤,則可以從/home/www/目錄跳轉到/home/wwwoldjun/目錄。
舉個滲透實例,某idc商在租用虛擬主機的時候如此分配空間/home/wwwroot/userxxx/、/home/wwwroot/useryyy/...,而open_basedir是這樣錯誤配置的:/tmp:/home/wwwroot/userxxx、/tmp:/home/wwwroot/useryyy。如果我們想通過配置的錯誤輕易滲透下userxxx站點,我們該怎麼做?
特殊值 . 指明腳本的工作目錄將被作為基准目錄。但這有些危險,因為腳本的工作目錄可以輕易被 chdir() 而改變。
在 httpd.conf 文件中中,open_basedir 可以像其它任何配置選項一樣用“php_admin_value open_basedir none”的方法關閉(例如某些虛擬主機中)。
在 Windows 中,用分號分隔目錄。在任何其它系統中用冒號分隔目錄。作為 Apache 模塊時,父目錄中的 open_basedir 路徑自動被繼承。
用 open_basedir 指定的限制實際上是前綴,不是目錄名。也就是說“open_basedir = /dir/incl”也會允許訪問“/dir/include”和“/dir/incls”,如果它們存在的話。如果要將訪問限制在僅為指定的目錄,用斜線結束路徑名。例如:“open_basedir = /dir/incl/”。