thinkphp rce审计重点在于动态调用、模板缓存、配置加载等隐蔽入口,而非仅搜索eval()或system();需重点关注call_user_func系列、invoke()、{php}标签、fetch参数校验、.env泄露及container::make类加载链。

ThinkPHP 项目里搜 eval() 或 system() 很难直接命中——因为框架层做了封装,危险操作往往藏在动态调用、反射、模板解析或配置加载逻辑中。真正要命的不是裸函数,而是 ThinkPHP 自己提供的、允许用户控制执行流的接口。
ThinkPHP 5/6 中的 call_user_func 系列调用点
ThinkPHP 路由解析、中间件注册、回调绑定等地方大量使用 call_user_func、call_user_func_array 和 invoke。如果参数来自请求(比如 $_GET['callback']、route 配置值、middleware 数组键),就可能被带入执行链。
- 常见触发位置:
thinkRoute::parseUrl()中对callback参数的处理;thinkacadeCache::tag()的 tag 名若未过滤,可能进入__call()反射调用 - ThinkPHP 6.0+ 的
thinkContainer::invokeClass()若传入用户可控类名(如通过config('app.app_middleware')动态加载),且该类构造函数/方法含敏感操作,就会绕过常规函数扫描 - 不要只盯
call_user_func字面,还要查->invoke()、::{$method}()这类动态方法调用,尤其出现在thinkconsoleCommand子类的execute()方法中
模板引擎中的 {php} 标签与模板缓存写入
ThinkPHP 默认关闭 {php} 标签,但很多老项目或自定义配置里会开启 tpl_deny_php 为 false,或在 view_replace_str 中误写入可执行内容。更隐蔽的是模板缓存文件本身:如果模板路径可控(如 fetch($_GET['tpl'])),且缓存目录可写,攻击者可诱导生成恶意缓存文件(如 runtime/view/xxx.php),下次访问直接触发 include 执行。
- 检查
config/template.php中'tpl_deny_php' => false或'strip_space' => false配合恶意注释绕过 - 搜索
fetch(、display(、view()->fetch(,确认参数是否经过白名单校验(如in_array($tpl, ['index', 'user'])) - ThinkPHP 5.1 的
think emplatedriverFile::write()若传入用户控制的$content,且未过滤 PHP 标签,缓存即为后门
配置加载与环境变量注入
ThinkPHP 支持从 .env、数据库、Redis 加载配置,而 Env::get()、Config::set()、Db::name()->where()->find() 返回值若直接进 eval()、unserialize() 或反射类名,就会形成 RCE 链。最典型的是「配置覆盖」漏洞:当 app_debug = true 且存在未过滤的 $_POST['config'] 接口时,攻击者可提交 config[database][hostname]=127.0.0.1;phpinfo(); 触发代码执行(依赖底层 PDO 构造函数解析)。
- 重点审计所有接受数组参数并调用
Config::set()的控制器方法,尤其是后台「系统设置」「通用配置」模块 - 检查
.env文件是否被 Web 目录直接暴露(如可通过/env访问),里面常含数据库密码、密钥等敏感信息 - ThinkPHP 6 的
thinkhelperStr::random()不危险,但若其返回值拼进shell_exec("md5sum {$str}.txt"),且$str含用户输入,就会触发命令注入
真正卡住审计进度的,从来不是找不到 eval(),而是没意识到 Container::make('app\command\' . input('c')) 这种写法等于把路由参数直通类加载器——只要 input('c') 能控制字符串内容,就能实例化任意命令类,而其中某个类的 handle() 方法里,可能就藏着一行没加校验的 file_get_contents($path) 或 shell_exec($cmd)。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











