ThinkPHP 5.0/5.1 文件包含漏洞原理与复现
ThinkPHP 5.x 版本中存在因模板引擎变量覆盖导致的文件包含漏洞。当控制器使用`$this->assign(request()->get())`直接将GET参数赋给模板变量,且未过滤`cacheFile`键时,攻击者可通过构造特定URL触发`extract()`函数覆盖内部变量,进而读取服务器任意文件。本文解析漏洞原理并提供复现步骤。
什么是文件包含漏洞
文件包含漏洞(File Inclusion)是指由于Web应用将用户输入的文件名作为参数传递,且未进行严格的安全校验,导致攻击者能够包含并执行非预期的文件。在PHP环境中,常见的包含函数包括:
虽然框架常通过包含模板文件来渲染页面,但如果逻辑处理不当,攻击者便可利用此机制包含系统中的敏感配置文件、日志文件或恶意上传的脚本文件,从而造成信息泄露或远程代码执行。
漏洞简述
在 ThinkPHP 5.0 至 5.0.18 以及 5.1.0 至 5.1.10 版本中,模板引擎在处理变量赋值时存在逻辑缺陷。核心问题在于 `View` 类使用的 `array_merge` 和 `extract` 函数缺乏对特定内部变量(如 `cacheFile`)的保护。当开发者编写代码如下时:
public function index()
{
$this->assign(request()->get());
return $this->fetch();
}`request()->get()` 会将所有 GET 请求参数直接作为视图变量传递给模板,而未做白名单过滤。这为后续的变量覆盖攻击敞开了大门。
漏洞复现与原理分析
第1步:环境搭建
首先需要下载受影响的 ThinkPHP 5.0 或 5.1.10 版本并配置好 PHP 运行环境。新建一个应用模块,确保可以正常访问首页。
第2步:植入测试代码
修改入口控制器 `application/index/controller/Index.php`,引入上述危险的赋值逻辑,以便触发漏洞:
<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller
{
public function index()
{
// 直接将GET参数分配给视图变量
$this->assign(request()->get());
return $this->fetch();
}
}并在 `application/index/view/index/index.html` 创建对应的空白模板文件,保存后即可开始审计。
第3步:跟进 `assign` 方法
从控制器的 `assign` 方法进入源码,查看变量如何存入视图对象。最终数据会流向 `View` 类的私有属性 `$data`。
第4步:分析变量合并过程
在 `library/think/View.php` 中,关注 `fetch` 方法的执行流程。系统在准备渲染模板前,会尝试合并配置和变量。此时会调用 `array_merge` 等数组操作函数:
该部分代码主要负责将外部传入的变量与默认变量进行合并,为后续提取做准备:
第5步:追踪到 `read` 方法的危险点
经过层层调用,代码最终执行到 `think\template\driver\file` 类的 `read` 方法。这里定义了模板路径解析逻辑,并且调用了 `extract` 函数将变量数组提取到当前作用域:
第6步:利用 `extract` 实现变量覆盖
在 `File.php` 的 `read` 方法末尾,存在一段关键代码:
extract($vars, EXTR_OVERWRITE);
EXTR_OVERWRITE 表示如果数组中的键已存在,则会覆盖原有的变量。由于 `$vars` 来自上文的 `request()->get()`,攻击者可以在 URL 中指定任意变量名。
系统原本期望的模板路径是通过 `$this->config['view_path']` 等硬编码方式确定的,但 `extract` 允许我们覆盖这个内部路径变量。具体规则如下:
如果我们在参数中传入 `cacheFile`(即 `?cacheFile=xxx`),由于 `EXTR_OVERWRITE` 的存在,它会覆盖掉系统内部用于指定合法模板位置的变量,从而导致 `include/include_once` 读取了攻击者指定的任意文件。
第7步:执行利用
为了验证漏洞,首先在网站的 `public` 目录下放置一张包含 `` 代码的图片木马(例如命名为 `test.jpg`)。然后构造请求:
http://target/public/?cacheFile=../public/test.jpg
浏览器成功返回 PHPInfo 信息,证明文件包含成功:
修复方案
针对此漏洞,开发人员应在调用 `assign` 时严格限制传入的参数范围,或对特定敏感字段进行清理。以下是几种修复思路:
- 过滤参数: 在传给 `assign` 之前,手动剔除 `$_GET` 中的 `cacheFile`、`_think_cachefile` 等可能影响内部逻辑的键值。
- 避免全局传参: 不要直接使用
$this->assign(request()->get())。应明确指定需要传递的变量,例如:$this->assign('name', input('name'));。 - 升级版本: ThinkPHP 官方已在后续版本中对 `Template` 类进行了重构,修复了 `EXTR_OVERWRITE` 的使用风险,建议升级到最新稳定版。
总结
ThinkPHP 5 文件包含漏洞是典型的因“信任用户输入”且“使用了宽松变量提取策略”导致的安全事故。它提醒我们在 Web 开发中,必须对框架内部使用的变量名保持警惕,绝不能将不可信的 GET/POST 参数直接映射到框架底层的方法参数中。以上就是 ThinkPHP 5 文件包含漏洞原理与复现的详细内容,更多关于 PHP 框架安全的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









