ThinkPHP 6.1.0 审计入口应从 think 命令行脚本和 app/Http/Middleware.php 中间件链开始,而非 index.php;所有请求经 think\Container 解析,关键污染源为 $request->param()、input() 和 $this->request->routeInfo() 返回值。

从哪里开始找入口点
ThinkPHP 6.1.0 的审计入口不是 index.php,而是 think 命令行脚本和 app/Http/Middleware.php 中的中间件链。框架启动后,所有请求先经由 think\Container 解析路由与控制器,因此关键污染源集中在 $request->param()、input() 和 $this->request->routeInfo() 这三类调用返回值上。不要从控制器方法名入手,先全局搜索这三处函数调用,再逐个检查其参数是否被直接用于危险操作。
重点关注动态类名和魔术方法调用
ThinkPHP 6.1.0 中大量使用 __call()、__invoke() 和 __get() 实现门面(Facade)和容器代理,这些是 POP 链高发区。尤其注意以下模式:
-
new $className()出现在app/common.php或命令行Command类中,且$className来自input('plugin')或路由参数 -
$obj->$method()调用中,$method是$request->param('action')直接赋值,未经过白名单校验 -
call_user_func_array([$obj, $callback], $args)的$callback可控,且$obj是框架内置类(如think\Cache、think\Log)
这类调用一旦失控,可能触发 think\cache\driver\File::set() 写入恶意缓存,或通过 think\log\driver\File::write() 覆盖日志文件实现 webshell。
别忽略缓存与日志路径拼接漏洞
CVE-2025-63888 在 6.1.0 中仍未彻底修复,核心问题是 view() 和 Cache::tag()->get() 等接口允许用户控制路径片段,而框架默认只拦截开头的 ../,不校验中间或结尾的跳转。例如:
立即学习“PHP免费学习笔记(深入)”;
view('../runtime/cache/' . input('name'))
当 input('name') 为 ../../public/shell.php 时,仍可绕过。更隐蔽的是日志驱动中的 think\log\driver\File::write(),若业务代码将 input('log_type') 拼入日志路径,攻击者可构造 log_type=../../../wwwroot/shell,配合 PHP 文件解析漏洞写入可执行内容。
反序列化链要盯紧 TagCache 和 SessionStore
ThinkPHP 6.0.12 引入的 think\cache\driver\TagCache 在 6.1.0 中仍是重灾区:其 serialize() 方法未对反序列化对象做类型限制,且 TagCache::get() 会自动反序列化缓存内容。如果攻击者能控制缓存键(比如通过可控的 Cache::tag(input('tag'))->get('key')),就可注入恶意序列化字符串。另一个风险点是 think\session\Store,6.1.0 默认仍使用 File 驱动,Store::setId() 若接收未经 ctype_alnum() 校验的 session_id,可能触发 file_put_contents() 写入任意路径——这不是旧版的任意删文件,而是结合 .user.ini 或 php-extension 特性实现静默持久化。
真正难处理的不是漏洞本身,而是这些调用往往藏在封装了三层以上的工具函数里,比如一个叫 parsePluginConfig() 的方法,内部调用了 eval(),但参数来源却是 config('plugin.rules'),而 config 又来自数据库字段。这种跨层污染必须靠符号表追踪才能定位,不能只靠关键词搜索。



















