<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    淺談從PHP內核層面防范PHP WebShell

                    By 咖啡(k4kup8_0x4154_gmail.com)
                    
                    
                    [目錄]
                    
                    1. 簡述
                    2. php的執行流程
                    3. php的生命周期
                    4. php源代碼分析以及功能性代碼的實現
                    5. 總結
                    6. 參考資料
                                                           
                    
                    一、簡述
                    
                        依據php特定運行環境、php某些特定函數缺陷、php普通函數可以實現變化多端的php 
                    webshell,php版本的scanwebshell也不是太給力。php webshell功能最大化就是實現文件、
                    目錄、命令、數據庫等操作,這些都是基于php代碼實現的。把相關功能化的php函數運行參
                    數提取出來,然后做一個判斷,這樣就能從本質上防范php webshell,在php這個層面實現
                    其安全的最大化。這里介紹下通過編寫php擴展來實現這個思路,當然需要的話也可以重新
                    編譯php源代碼來實現。
                    
                        首先我們了解下php的執行流程、php生命周期,接下來通過分析具體函數的php源代碼
                    來實現功能性代碼。
                        	
                    
                    二、php的執行流程
                    
                    2.1 scanner
                        
                        將PHP代碼轉換為Tokens,詳見代碼Zend/zend_language_scanner.l。
                           
                    2.2 parser
                        
                        將Tokens轉換成表達式,詳見代碼Zend/zend_language_parser.y。
                           
                    2.3 compile
                        
                        將表達式編譯成opcode。opcode存放在op_array中。
                           
                    2.4 execute
                        
                        Zend Engine調用zend_execute來執行op_array,輸出結果。
                    
                    
                    三、php的生命周期
                    
                    3.1 STARTUP
                    
                        1、初始化引擎和核心組件。
                        2、解析php.ini。
                        3、初始化靜態構建的模塊(MINIT)。
                        4、初始化共享模塊(MINIT)。
                    
                    3.2 ACTIVATION
                    
                        1、初始化環境變量、變量。
                        2、激活靜態構建的模塊(RINIT) 。
                        3、激活共享模塊(RINIT) 。
                        
                    3.3 RUNTIME
                        
                        1、編譯和執行php.ini中auto_prepend_file選項指定的文件。
                        2、編譯和執行所請求的文件。
                        3、編譯和執行php.ini中auto_append_file選項指定的文件。
                        
                    3.4 DEACTIVATION
                          
                        1、調用用戶指定的退出函數。
                        2、銷毀對象實例。
                        3、停用模塊(RSHUTDOWN)。
                        4、清空輸出。
                        5、清理環境。
                        6、釋放剩余的非持久內存。
                    
                    3.5 SHUTDOWN
                          
                        1、關閉啟動的全部模塊(MSHUTDOWN)。
                        2、關閉引擎。
                    
                    
                    四、php源代碼分析以及功能性代碼的實現
                        
                        php函數分為兩種:一種是Zend的函數,這類函數數量比較少,比如eval函數。第二種
                    是由PHP_FUNCTION宏編寫的,這類函數數量比較多,比如system函數。實現對兩類函數在提
                    取運行時的參數的方式也不相同,比如處理eval函數用重寫zend_compile_string的方式,
                    而處理system函數則對HashTable操作。下邊就以eval函數和system函數為例進行分析、代
                    碼實現。
                       
                    4.1 eval函數代碼分析與代碼實現
                        
                        首先我們看php源代碼中eval函數是如何實現的,部分代碼如下:
                    
                    --code-------------------------------------------------------------------------                     
                        // PHPSRC/Zend/zend_vm_def.h
                            
                        if (inc_filename->type!=IS_STRING) {
                    		tmp_inc_filename = *inc_filename;
                    		zval_copy_ctor(&tmp_inc_filename);
                    		convert_to_string(&tmp_inc_filename);
                    		inc_filename = &tmp_inc_filename;
                    	}    
                            
                        case ZEND_EVAL: {
                                    /* 調用zend_make_compiled_string_description函數 */
                    				char *eval_desc = zend_make_compiled_string_description("eval()"d code" TSRMLS_CC);
                                    /* 調用zend_compile_string函數 */
                    				new_op_array = zend_compile_string(inc_filename, eval_desc TSRMLS_CC);
                    				efree(eval_desc);
                    			}
                        /* 執行op_array */
                        zend_execute(new_op_array TSRMLS_CC);
                        
                        //PHPSRC/Zend/zend.c
                             
                        #define COMPILED_STRING_DESCRIPTION_FORMAT "%s(%d) : %s"
                        ZEND_API char *zend_make_compiled_string_description(char *name TSRMLS_DC)
                        {
                        	zend_spprintf(&compiled_string_description, 0, COMPILED_STRING_DESCRIPTION_FORMAT, cur_filename, cur_lineno, name);
                        	return compiled_string_description; //返回值包含"eval()"d code"字符串
                        }
                     
                        //PHPSRC/Zend/zend_compile.c
                             
                        ZEND_API zend_op_array *(*zend_compile_string)(zval *source_string, char *filename TSRMLS_DC);
                        
                        zend_compile_string一個函數指針。下邊看下引擎初始化的時候對zend_compile_string的操作。
                            
                        int zend_startup(zend_utility_functions *utility_functions, char **extensions, int start_builtin_functions)
                        {
                        	zend_compile_string = compile_string; //對zend_compile_string函數的地址賦值
                    -------------------------------------------------------------------------------    	
                                
                        只要檢查op_array中是否含有"eval()"d code"字符串,就能判斷是否是在執行eval函數。
                        在引擎初始化的時候,默認會將compile_string函數的地址賦值給zend_compile_string,
                    compile_string函數則返回一個指向zend_op_array的指針。如果能在php代碼編譯之前對
                    zend_compile_string進行重寫,那么就能達到劫持的目的。根據php的生命周期,對
                    zend_compile_string進行重寫應該放在STARTUP或者ACTIVATION這兩個階段,而編寫php擴
                    展所使用到的PHP_MINIT_FUNCTION和PHP_RINIT_FUNCTION宏就分別處在STARTUP和ACTIVATION
                    這個兩個階段,這是為什么呢?我們先看下php.h代碼中對PHP_MINIT_FUNCTION宏的定義。
                        
                    --code-------------------------------------------------------------------------    
                        #define PHP_MINIT_FUNCTION		ZEND_MODULE_STARTUP_D
                        //ZEND_MODULE_STARTUP_D定義在zend_API.h
                        #define ZEND_MODULE_STARTUP_D(module)		int ZEND_MODULE_STARTUP_N(module)(INIT_FUNC_ARGS)
                        //ZEND_MODULE_STARTUP_N定義在zend_API.h
                        #define ZEND_MODULE_STARTUP_N(module)       zm_startup_##module
                        //INIT_FUNC_ARGS定義在zend_modules.h
                        #define INIT_FUNC_ARGS		int type, int module_number TSRMLS_DC
                    -------------------------------------------------------------------------------
                        
                        PHP_MINIT_FUNCTION(module)的原型就是:
                    
                    --code-------------------------------------------------------------------------    
                    zm_startup_module(int type, int module_number TSRMLS_DC)
                    -------------------------------------------------------------------------------
                    
                        同樣的PHP_RINIT_FUNCTION(module)的原型為:
                        
                    --code-------------------------------------------------------------------------     
                    zm_activate_module(int type, int module_number TSRMLS_DC)
                    -------------------------------------------------------------------------------
                    
                        關于對eval函數運行參數截取分析的實現代碼如下:
                    
                    --code-------------------------------------------------------------------------
                        #define OVECCOUNT 30
                        /* 具體正則表達式要按照具體的需求來寫,下面正則僅為測試用 */
                        #define eval_regex_value   "(((chr\\(\\d*?\\)|base64_decode\\(|eval|gzinflate\\(|system|shell_exec|popen|pclose|proc_close|proc_get_status|proc_nice|proc_terminate|exec|passthru|show_source|escapeshellcmd|escapeshellarg system|shell_exec|popen|pclose|proc_open|proc_close|proc_get_status|proc_nice|proc_terminate|exec|passthru|show_source|escapeshellcmd|escapeshellarg)\\([{}"$\\w\\s]*?\\));).*?"
                        
                        static zend_op_array* (*old_compile_string)(zval *source_string, char *filename TSRMLS_DC);
                        static zend_op_array* safe_compile_string(zval *source_string, char *filename TSRMLS_DC);
                        
                        PHP_RINIT_FUNCTION(safe) //PHP_MINIT_FUNCTION(safe)也可
                        {
                        	safe_hook_execute();
                        	return SUCCESS;
                        }
                        
                        PHP_RSHUTDOWN_FUNCTION(safe) //PHP_MSHUTDOWN_FUNCTION(safe)也可
                        {
                        	safe_unhook_execute();
                        	return SUCCESS;
                        }   
                        
                        int matchpattern(char *src, char *pattern, int i) //正則匹配函數
                        {              
                        	pcre *re;
                        	const char *error;
                        	int erroffset;
                        	int ovector[OVECCOUNT];
                        	int rc;
                        	char *substring_start;
                        	int substring_length;
                        	TSRMLS_FETCH();
                        	
                        	re = pcre_compile(pattern, PCRE_CASELESS|PCRE_DOTALL, &error, &erroffset, NULL);
                        	if(re == NULL) {
                        		//printf("PCRE compilation failed at offset %d: %s\n", erroffset, error);
                        		return 1;
                        	}
                        	
                        	rc = pcre_exec(re, NULL, src, strlen(src), 0, 0, ovector, OVECCOUNT);	
                        	if(rc >= 0) {
                        		substring_start = src + ovector[2*i];
                        		substring_length = ovector[2*i+1] - ovector[2*i];
                        		
                        		printf("Match_result: %.*s\n", substring_length, substring_start);
                        		printf("Filename    : %-40s\n", zend_get_executed_filename(TSRMLS_C));
                        		printf("Line        : %-50i\n", zend_get_executed_lineno(TSRMLS_C));
                        	}
                        	free(re);
                        	return rc;
                        }    
                        
                        static zend_op_array *safe_compile_string(zval *source_string, char *filename TSRMLS_DC)
                        {
                        	char *eval_strings;
                        	int x;
                            zend_op_array *op_array;
                         	
                            op_array = old_compile_string(source_string, filename TSRMLS_CC);
                        
                            /* 過濾非eval函數 */
                        	if(!strstr(op_array->filename, "eval()"d code")) {
                        		return old_compile_string(source_string, filename TSRMLS_CC);
                        	}
                        	/* 將source_string字符串賦值給eval_strings */
                        	eval_strings = estrndup(Z_STRVAL_P(source_string), Z_STRLEN_P(source_string));
                        
                        	printf("%s","\n");
                        	printf("Function    : %-40s\n", "eval");	
                        	x = matchpattern(eval_strings, eval_regex_value, 1);
                        	if (x < 0)
                        	{
                        		return old_compile_string(source_string, filename TSRMLS_CC);
                        	}
                        	else if(x >= 0)
                        		return FALSE;
                        }
                        
                        int safe_hook_execute()
                        { 			
                        	old_compile_string = zend_compile_string;
                        	zend_compile_string = safe_compile_string;
                            system_hook_system();  //對應后邊對system函數的操作   
                        	return TRUE;
                        }
                        
                        int safe_unhook_execute()
                        {	
                        	zend_compile_string = old_compile_string;    
                        	return TRUE;
                        }
                    -------------------------------------------------------------------------------    
                        
                    4.2 system函數代碼分析與代碼實現 
                        
                        首先我們看php源代碼中system函數是如何實現的,部分代碼如下:
                    
                    --code-------------------------------------------------------------------------         
                        //PHPSRC\ext\standard\exec.c
                          
                        PHP_FUNCTION(system)
                        {  
                            /* 調用php_exec_ex函數 */
                            php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
                        }
                           
                        static void php_exec_ex(INTERNAL_FUNCTION_PARAMETERS, int mode)
                        {
                        	char *cmd;
                    
                        	if (!ret_array) {
                    		/* 調用php_exec函數 */
                        		ret = php_exec(mode, cmd, NULL, return_value TSRMLS_CC);
                        	} else {
                        		if (Z_TYPE_P(ret_array) != IS_ARRAY) {
                        			zval_dtor(ret_array);
                        			array_init(ret_array);
                        		}
                        		ret = php_exec(2, cmd, ret_array, return_value TSRMLS_CC);
                    -------------------------------------------------------------------------------
                    
                        接下來看php_exec函數的定義。
                    
                    --code-------------------------------------------------------------------------  
                        int php_exec(int type, char *cmd, zval *array, zval *return_value TSRMLS_DC)
                        {
                        	char *cmd_p, *b, *c, *d=NULL;
                    
                        	if (PG(safe_mode)) {
                        		cmd_p = php_escape_shell_cmd(d);
                        		efree(d);
                        		d = cmd_p;
                        	} else {
                        		cmd_p = cmd;
                    
                            #ifdef PHP_WIN32
                        	    fp = VCWD_POPEN(cmd_p, "rb"); //調用VCWD_POPEN函數
                    -------------------------------------------------------------------------------    	    
                     
                        接下來看VCWD_POPEN函數的定義。
                    
                    --code-------------------------------------------------------------------------     
                        //TSRM\tsrm_virtual_cwd.c
                        
                        #ifdef TSRM_WIN32 //以windows平臺為例
                        
                        CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC)
                        {
                        	return popen_ex(command, type, CWDG(cwd).cwd, NULL);//調用popen_ex函數
                        }
                    -------------------------------------------------------------------------------
                        
                        接下來看popen_ex函數的定義。
                        
                    --code-------------------------------------------------------------------------
                    //TSRM\tsrm_win32.c
                    
                    TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env)
                    {
                    	char *cmd;
                        
                    	cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c "));
                    	sprintf(cmd, "%s /c %s", TWG(comspec), command);
                    	if (!CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW, env, cwd, &startup, &process)) {
                    		return NULL;
                    	}
                    	free(cmd); 	
                    -------------------------------------------------------------------------------    	
                    
                        上邊是system函數執行的參數傳遞的過程,在這個過程中如果可以截取函數執行的參數
                    的話,就可以分析參數是否包含危險的關鍵字。為了方便編寫擴展程序,我們直接在exec.c
                    中php_exec函數中添加截取代碼,也就是在php_exec_ex函數調用php_exec函數之前的位置。
                    添加如下代碼即可獲取執行的參數:
                    
                    --code-------------------------------------------------------------------------   
                       	x = matchpattern(cmd, system_regex_value, 0);//調用正則函數進行判斷
                    	if(x >= 0) RETURN_FALSE;
                    -------------------------------------------------------------------------------
                    	
                        下邊我們分析怎么實現對system函數的重寫,這里主要參照main.c文件中實現php.ini
                    中disable_functions功能的php_disable_functions函數,它調用了zend_disable_function。
                    
                    --code-------------------------------------------------------------------------   
                    ZEND_API int zend_disable_function(char *function_name, uint function_name_length TSRMLS_DC)
                    {
                    	if (zend_hash_del(CG(function_table), function_name, function_name_length+1)==FAILURE) {
                    		return FAILURE;
                    	}
                    	disabled_function[0].fname = function_name;
                    	return zend_register_functions(NULL, disabled_function, CG(function_table), MODULE_PERSISTENT TSRMLS_CC);
                    }
                    -------------------------------------------------------------------------------
                        
                        同樣我們可以用zend_hash_del函數將system從function_table中刪除,然后注冊新的
                    zend函數,以達到對system函數劫持的目的。
                    
                        system作為一個執行系統命令的函數,在這里進行了禁用操作,沒有使用正則處理函數
                    進行參數的檢查,當然也可以根據具體的需求進行具體的操作。
                      
                        實現代碼如下:
                        
                    --code-------------------------------------------------------------------------    
                        /* 聲明導出函數 */
                        PHP_FUNCTION(system1)
                        {  
                        	printf("%s","\n");
                        	printf("Function    : %-40s\n", "system");
                        	printf("Filename    : %-40s\n", zend_get_executed_filename(TSRMLS_C));
                        	printf("Line        : %-50i\n", zend_get_executed_lineno(TSRMLS_C));
                        	printf("%s","system function is disabled.");  
                        }
                        
                        /* 聲明 Zend 函數塊 */
                        zend_function_entry hook_system_functions[] = {      
                          PHP_FALIAS(system, system1, NULL) // 創建system別名
                          {NULL, NULL, NULL}
                        };
                        
                        /* 創建system hook函數 */
                        int safe_hook_system()
                        {
                          TSRMLS_FETCH();
                          /* 刪除function_table中的system函數 */
                          zend_hash_del(CG(function_table), "system", sizeof("system"));
                        
                        /* 注冊新zend函數 */
                        #ifndef ZEND_ENGINE_2
                          zend_register_functions(hook_system_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
                        #else
                          zend_register_functions(NULL, hook_system_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
                        #endif
                          return 0;
                        }
                    -------------------------------------------------------------------------------
                        
                    4.3 demo運行效果
                        
                    4.3.1 加載php擴展
                    
                    -------------------------------------------------------------------------------
                    C:\phpext>type php.ini | findstr "^extension="
                    extension=php_safe.dll 
                    
                    C:\phpext>php 3.php
                    
                    Function    : system
                    Filename    : C:\phpext\3.php
                    Line        : 3
                    
                    system function is disabled.
                    
                    Function    : eval
                    Match_result: exec("ver");
                    Filename    : C:\phpext\3.php
                    Line        : 9
                    -------------------------------------------------------------------------------
                    
                    4.3.2 不加載php擴展   
                        
                    -------------------------------------------------------------------------------
                    C:\phpext>type php.ini | findstr "^extension="
                    
                    
                    C:\phpext>php 3.php
                    
                    Microsoft Windows XP [版本 5.1.2600]
                    -------------------------------------------------------------------------------
                    
                     
                    五、總結
                        
                        實現php webshell的功能性函數眾多,我們做到控制關鍵性的函數足以。當然實現函數
                    截取要根據函數的情況進行一一的分析,然后做相應的判斷。
                    
                        最后要感謝下SuperHei,文章不足之處請斧正。
                    
                    
                    六、參考資料
                    
                    [1] php源代碼  http://www.php.net
                    [2] PHP Extension Writing http://talks.somabo.de/200903_montreal_php_extension_writing.pdf
                    
                    -EOF-
                    

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频