ThinkPHP 5.0 远程代码执行漏洞是控制器路由解析、Request::method() 处理、参数自动绑定三处缺陷叠加形成的稳定利用链;需同步实施入口拦截、白名单校验和配置收紧,仅升级至5.0.24不足以防护。

ThinkPHP 5.0 远程代码执行漏洞不是“某个函数没过滤”那么简单,而是由控制器路由解析、Request::method() 处理、参数自动绑定三处缺陷叠加形成的可稳定利用链;仅升级到 5.0.24 不足以闭环防护,必须同步做入口拦截、白名单校验和配置收紧。
控制器名未校验导致任意方法调用
漏洞根源在于 thinkApp::module() 解析 s= 参数时,直接将用户输入拼入类名并反射调用,例如 s=index/thinkpp/invokefunction 会尝试实例化 thinkppinvokefunction 类——而该类在框架中真实存在且 __invoke 方法接受任意 callable。
- 典型 payload:
?s=index/ hinkpp/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id - 触发前提是未开启强制路由(
'url_route_on' => false或默认配置) - 修复不能只靠正则过滤字母数字,必须限制命名空间前缀,例如只允许
^[a-zA-Z][a-zA-Z0-9]*$,禁止反斜杠和点号. - 若项目已启用路由,仍需在
App::module()中间加一层校验,否则攻击者可绕过路由直接走s=入口
Request::method() 被滥用触发魔术方法调用
当 $_POST['_method'] 存在时,thinkRequest::method() 会把该值赋给 $this->method,后续再通过 $this->{$this->method}($_POST) 动态调用方法。攻击者传入 _method=__construct,就能让整个 $_POST 数组作为参数进入构造函数,进而触发 filter、server 等可控数组的处理逻辑。
- 关键利用条件:
Config::get('var_method')默认为_method,且未被显式清空 - 常见 payload:
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami - 修复必须重写
Request::method(),对$method做白名单限制,仅允许GET、POST、PUT、DELETE、PATCH - 同时在
config/app.php中显式设'var_method' => '',彻底禁用该变量映射
入口文件硬性拦截高危请求模式
补丁代码可能被绕过,比如开发者自定义了中间件或重写了路由逻辑,因此必须在最外层(public/index.php)做第一道防线,用正则直接阻断已知攻击特征。
立即学习“PHP免费学习笔记(深入)”;
- 在
index.php开头立即插入:if (isset($_GET['s']) && preg_match('/\\think\\|invokefunction|call_user_func/i', $_GET['s'])) { die('Access Denied'); } - 注意双反斜杠
\\是 PHP 字符串转义后的结果,实际匹配的是hink - 同样要拦截
php://filter、phar://等协议关键字,防止反序列化链路被激活 - 该规则必须放在
require框架入口之前,否则已被加载的类可能提前触发漏洞
配套配置项不关闭,修复等于白做
很多团队加了代码补丁却仍被攻破,问题出在配置残留:调试模式开着、自动路由开着、缓存目录可读写、session 存储在文件里——这些都会成为漏洞利用的放大器。
-
app_debug必须设为false,否则报错会泄露路径、类名、甚至数据库密码 -
auto_route必须设为false,route_complete_match设为true,避免模糊匹配引入意外入口 -
runtime目录权限应设为750且 Web 用户不可写,public下禁止执行 PHP(通过.htaccess或 Nginxlocation ~ .php$限制) - 如果用了 Memcached 或 Redis 存 session,确认其未启用反序列化驱动(如
session.serialize_handler = php_serialize)
真正难的不是加几行代码,而是确认所有配置项、所有中间件、所有自定义 Request/Response 类都未绕过你加的校验——任何一处遗漏,都可能让整个加固失效。



















