先看一段我們不考慮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 admin@zf.com 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 = $_GET['file']; preg_match("/^img/", $file) or die('error_file'); $file='/home/www/upload/'.$file; file_exists($file) or die('no_such_file'); $f = fopen("$file", 'r'); $jpeg = fread($f, filesize("$file")); fclose($f); Header("Content-type: image/jpeg"); Header("Content-disposition: inline; filename=test.jpg"); echo $jpeg; ?>
|
雖然file是任意提交的,但是限制了首碼必須為img,我們如果想跳出目錄讀檔案,比如,讀取網站根目錄下的config.php,我們得提交?file=img/../../config.php,但是此處有個限制條件,就是upload目錄下不存在img檔案夾,在windows檔案系統裡,系統不會去考慮目錄存在不存在,會直接跳轉目錄從而導致漏洞;但linux檔案系統非常嚴謹,它會仔細判斷每一層目錄是否存在,比如這裡由於不存在img,則跳出去讀取檔案的時候直接報錯。看如下一個示意圖:
再看一個類似的當地套件含的例子:
代碼如下 |
複製代碼 |
<?php include "aaa".$_GET['lang'].".php"; ?>
|
由於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/”。