thinkphp不存在官方定义的“文件重命名漏洞”,但因业务代码对用户输入的文件名/路径校验缺失,易导致任意文件覆盖、目录遍历或webshell写入;常见于move_uploaded_file()直接拼接用户参数、模板缓存动态命名、日志路径污染三类场景。

ThinkPHP本身没有官方定义的“文件重命名漏洞”,但实际攻防中常出现因框架或业务代码对用户输入的文件名、路径处理不当,导致的任意文件覆盖、目录遍历、WebShell写入等高危行为——这类问题在安全报告中常被泛称为“文件重命名类漏洞”。其本质是未校验、未过滤、未隔离用户可控的文件名参数,使攻击者可通过构造特殊字符串(如../、.php、%2e%2e%2f、空字节%00等)绕过限制,实现越权写入或覆盖关键文件。
哪些位置容易出问题
以下三类场景最常触发重命名/覆盖风险:
-
上传模块调用 move_uploaded_file() 时直接拼接用户传入的 $filename:例如
move_uploaded_file($tmp, './uploads/' . $_POST['filename']);,攻击者传filename=../../public/shell.php即可写入 Web 目录 -
模板渲染或缓存写入使用动态文件名:如 ThinkPHP 3.x 的
assign()配合自定义模板引擎,若将用户输入作为缓存文件名且未清理路径字符,可能覆盖Runtime/Cache/下的 PHP 文件 -
日志/调试功能写入路径可被污染:部分旧版或定制中间件会把
$_SERVER['REQUEST_URI']或$_GET参数直接拼进日志文件名,配合目录遍历可写入任意位置
如何快速识别是否存在风险
不依赖扫描器,手动验证更可靠:
- 在上传表单或文件操作接口中,尝试提交
filename=test.php%00.jpg,观察是否生成test.php(空字节截断) - 提交
filename=../../../etc/passwd,检查响应或服务器端文件是否异常创建 - 查看代码中所有
move_uploaded_file、file_put_contents、fopen(..., 'w')调用点,确认目标路径是否含未经处理的用户变量
修复必须做的三件事
不是加个 str_replace(['..','/'], '', $name) 就完事——这种过滤极易被编码绕过(如 %2e%2e%2f、....//)。正确做法是:
-
白名单校验扩展名:只允许
['jpg', 'png', 'pdf']等明确业务需要的后缀,用pathinfo($filename, PATHINFO_EXTENSION)提取并比对,拒绝一切非白名单值 -
强制重命名 + 去路径化:丢弃用户传来的原始文件名,用
uniqid() . '.' . $safe_ext生成新名;再用basename()提取最终文件名,彻底剥离路径成分 -
写入前校验真实路径:调用
realpath($target_path),然后判断其是否以你预期的根目录(如/var/www/html/uploads/)开头。不匹配则拒绝写入
ThinkPHP 特定版本需额外注意
某些历史版本存在框架层隐患,即使业务代码规范也可能被绕过:
-
ThinkPHP 5.0.x ~ 5.1.23:路由解析未严格限制控制器名,攻击者可传
s=index/\think\template\driver\file/write&cacheFile=shell.php&content=xxx直接写入 PHP 文件——必须升级至 5.0.24+ 或 5.1.24+,或按官方补丁手动加固think\App::module() -
ThinkPHP 3.2.x:
Think\Template\Driver\File::loadTemplate()若开启模板缓存且用户可控C('TMPL_CACHE_ON'),可能通过恶意模板名触发路径穿越——关闭模板缓存或升级到 3.2.4+ 并打补丁 -
所有版本通用风险:若项目启用了多语言包加载(
Lang::load()),且语言参数(如?lang=zh-cn)未做realpath白名单校验,攻击者可传lang=../../../public/shell导致写入
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











