频道栏目
首页 > 资讯 > 网站安全 > 正文

浅谈从PHP内核层面防范PHP WebShell

11-01-15        来源:[db:作者]  
收藏   我要投稿

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函数是如何实现的,部分代码如下:

    // 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宏的定义。

    #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函数运行参数截取分析的实现代码如下:

    #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;
   

相关TAG标签
上一篇:浅谈javascript函数劫持
下一篇:华夏外挂下载系统2.0漏洞解析
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站