thinkphp5 漏洞原理分析合集

thinkphp5 rce原理分析

文末附赠相关综合利用工具

文章中主要介绍以下4种漏洞的原理分析过程:

5.0.X 路由过滤不严谨rce
5.1.X 路由过滤不严谨rce
__construct 变量覆盖导致RCE

5.0.X 数据库信息泄露

1. 5.0.x 路由过滤不严谨导致rce

1.1 原理分析

设置兼容模式(pathinfo)路由解析时,没有对”\“进行过滤,导致可以指定任意模块控制器和方法,例如:

1.
http://127.0.0.1/tp5.0/public/index.php?s=index/think/app/invokefunction
模块:index
控制器:think
方法:app
参数:invokefunction

正常解析会出错,因为没有think中没有app方法。


2.
http://127.0.0.1/tp5.0/public/index.php?s=index/think\app/invokefunction
模块:index
控制器:think\app
方法:invokefunction

加上"\"后就会将think\app解析成为一个整体,就是think下的app文件。

首先进入入口文件public下的index.php中:

thinkphp5 漏洞原理分析合集_第1张图片

进入start.php中,先加载base.php,再执行app.php中的run方法。

thinkphp5 漏洞原理分析合集_第2张图片

跟进run方法,前面为初始化的一些东西

thinkphp5 漏洞原理分析合集_第3张图片

到这开始进入路由解析,使用的routeCheck方法。

thinkphp5 漏洞原理分析合集_第4张图片

跟进routeCheck方法,先调用Request.php中的path方法,

thinkphp5 漏洞原理分析合集_第5张图片

跟进path方法,调用pathinfo方法,来判断pathinfo(是否为兼容模式)。

thinkphp5 漏洞原理分析合集_第6张图片

跟进pathinfo方法,Config::get(‘var_pathinfo’)查看是否配置了var_pathinfo参数,如果配置了为兼容模式,并提取出默认参数s的值,最后传到app.php中的$path。

thinkphp5 漏洞原理分析合集_第7张图片

下面$config[‘url_route_must’]为查看是否开启了强制路由,如果开启解析’\’时就会失败,就不会产生rce。

thinkphp5 漏洞原理分析合集_第8张图片

进入parseUrlPath方法中,该方法将路由拆分为模块、控制器、方法。这里面并没有过滤’\’。

thinkphp5 漏洞原理分析合集_第9张图片

返回将返回得值赋值给$path,如下图:

thinkphp5 漏洞原理分析合集_第10张图片

提取模块,提取$path中的模块”index”。

提取控制器和方法,提取$path中的控制器”think\app”和方法”invokefunction”。

thinkphp5 漏洞原理分析合集_第11张图片

将拆分的信息封装进$route中,再将$route以数组中值的方式返回给$dispatch。

thinkphp5 漏洞原理分析合集_第12张图片

thinkphp5 漏洞原理分析合集_第13张图片

这块是记录日志、监听、检查缓存,直接略过。 thinkphp5 漏洞原理分析合集_第14张图片

判断$dispatch中$type类型,选择执行module函数。

thinkphp5 漏洞原理分析合集_第15张图片

跟进静态module函数,一些初始化模块。

thinkphp5 漏洞原理分析合集_第16张图片

获取路由中的模块、控制器、方法。

thinkphp5 漏洞原理分析合集_第17张图片

thinkphp5 漏洞原理分析合集_第18张图片

执行控制器中的方法,调用静态方法invokeMethod。

thinkphp5 漏洞原理分析合集_第19张图片

实例化ReflectionMethod方法传入模块、控制器、方法,再通过Request类中的filterValue方法递归出传入的参数。

thinkphp5 漏洞原理分析合集_第20张图片

将递归出的参数,进行递归绑定,并写入$args中。

thinkphp5 漏洞原理分析合集_第21张图片

thinkphp5 漏洞原理分析合集_第22张图片

再调用反射的方法,并传入参数,也就是执行传入的恶意命令。

返回$data

thinkphp5 漏洞原理分析合集_第23张图片

1.3 payload

http://[ip]/tp5/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

2. 5.1.X 路由过滤不严谨rce

由于找不到5.1的低版本,所以只能分析漏洞关键点,大体流程和5.0差不多。

关键点:

路由没有过滤"\",导致可以通过"\"来指定任意模块和控制器。

2.1 原理分析

利用链同样是invokefuntion方法,在think/Container.php中,但与5.0不同的是方法中存在了call_user_func_array函数,该函数可以将出入的第一个值当作回调函数,第二个值为回调函数的参数。 bindParams方法为递归提取传入的参数绑定到$args上。

thinkphp5 漏洞原理分析合集_第24张图片

直接从路由解析那块开始分析,App.php的run方法,调用routeCheck()

thinkphp5 漏洞原理分析合集_第25张图片

跟进routeCheck(),$path将url中的模块控制器方法函数取出来,$must判断是否为强路由模式,$dispatch路由检测,检测模块控制。

thinkphp5 漏洞原理分析合集_第26张图片

thinkphp5 漏洞原理分析合集_第27张图片

routeCheck()返回$dispatch,回到App.php中,$this->routeCheck()->init()就等于$this->$dispatch->init()就等于think\route\dispatch\Url->init()

跟进到think\route\dispatch\Url.php的init()中,调用parseUrl()

thinkphp5 漏洞原理分析合集_第28张图片

跟进parseUrl()中,我这个版本是修复了的,所以多出来的那块代码就是修复代码,低版本中并没有,所以就没有过滤”\“,导致可以调用think/Container中的invokeFuntion方法,导致可以使用call_user_func_array函数执行恶意命令。

thinkphp5 漏洞原理分析合集_第29张图片

2.2 payload

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

3 __construct 变量覆盖导致RCE

以thinkphp5.0.x为例

关键点

Request.php中method()中,没有对'var_method'进行过滤,用户可控传入__constrct,导致调用__construct析构函数,将传入变量循环覆盖到$this->method="GET",$this->get="whoami",$this->filter="system";如果开启debug,会调用call_user_func导致rce。

3.1 原理分析

我们payload传入

POST http://127.0.0.1/tp5.0/public/index.php?s=index
_method=__construct&filter[]=system&method=GET&get[]=whoami

上面的那些操作就不说了,直奔主题,进行路由检测会调用静态方法routeCheck()

thinkphp5 漏洞原理分析合集_第30张图片

跟进又调用Route类里面的静态方法check()

thinkphp5 漏洞原理分析合集_第31张图片

跟进check(),又调用method()

thinkphp5 漏洞原理分析合集_第32张图片

跟进Request.php中method(),Config::get(‘var_method’)提权var_method中的值,var_method的又为_method,这个_method可控,我们传入’__construct’,到$this->{$this->method}($_POST);中就变为$this->__construct($POST)。

thinkphp5 漏洞原理分析合集_第33张图片

跟进__construst(),该方法将我们POST方法传入的参数通过foreach循环覆盖到相对应的参数上。

thinkphp5 漏洞原理分析合集_第34张图片

返回$method=‘get’。

到self::$rules[$method];,这也是为什么要传入method=GET的原因,因为不传入的的话$method=__construct,如果静态数组$rules内不存在该参数就会报错。

所以我们传入的method可以不是GET,只要在这个数组里面就行。 thinkphp5 漏洞原理分析合集_第35张图片

到下面判断是否开启debug调试模式,调用param()。 thinkphp5 漏洞原理分析合集_第36张图片

跟进Request.php中param(),,将POST中的参数赋值给$vars,又调用get()。

thinkphp5 漏洞原理分析合集_第37张图片

跟进get(),调用input(),传入刚才覆盖的$this->get。

thinkphp5 漏洞原理分析合集_第38张图片

跟进input(),将$this->get的值赋值给$data。

thinkphp5 漏洞原理分析合集_第39张图片

返回到param()中,又调用input()。

thinkphp5 漏洞原理分析合集_第40张图片

跟进input(),将$this->filter赋值给$filter。

thinkphp5 漏洞原理分析合集_第41张图片

再到下面通过array_walk_recursive函数,调用filterValue方法,将$data和$filter的值带入到filterValue方法中。

thinkphp5 漏洞原理分析合集_第42张图片

跟进filterValue(),使用call_user_func回调函数,回调$filter的值system函数,将$value用上面的array_walk_recursive函数遍历传入system函数中执行,并把执行结果传入到$data中。

thinkphp5 漏洞原理分析合集_第43张图片

thinkphp5 漏洞原理分析合集_第44张图片

再到下面,$dispatch[‘type’]的值为’module’,调用静态方法module()。

thinkphp5 漏洞原理分析合集_第45张图片

跟进module(),到最后调用静态方法invokeMethod()。

thinkphp5 漏洞原理分析合集_第46张图片

跟进invokeMethod(),调用静态方法bindParams()

thinkphp5 漏洞原理分析合集_第47张图片

跟进bindParams(),通过Config::get(‘url_param_type’)获取参数为0,调用Request::instance()->param(),又到了熟悉的param()了,后面的跟上面一样。

thinkphp5 漏洞原理分析合集_第48张图片 

将命令执行结果返回到$vars中。

thinkphp5 漏洞原理分析合集_第49张图片

为什么输出四个呢? 前两个为开启debug里面调用param()链,然后因为$values里面又两个’whoami’,所以遍历的时候执行了2次,后面的是module方法调用的param()链,后面也一样执行了两次,所以显示4个结果。

thinkphp5 漏洞原理分析合集_第50张图片

3.2 payload

POST http://127.0.0.1/tp5.0/public/index.php?s=index
_method=__construct&filter[]=system&method=GET&get[]=whoami

4 5.0.X 数据库信息泄露

关键点

也是路由过滤不严谨,导致调用任意类方法,这回只是利用链不一样了,调用think\Config中的get方法,读取配置文件。

4.1 原理分析

直奔关键代码部分,调用module()执行传入的方法。

thinkphp5 漏洞原理分析合集_第51张图片

thinkphp5 漏洞原理分析合集_第52张图片

跟进到module()中,到最后调用静态方法invokeMethod()执行操作。 thinkphp5 漏洞原理分析合集_第53张图片

跟进invokeMethod(),开始实例化反射类ReflectionFunction,传入$class和$method,也就是模块控制器方法,再用静态方法bindParams(),把参数绑定到$args中,也就是我们传入的”database.username”,最后调用invokeArgs将参数传入之前的反射类中执行。

thinkphp5 漏洞原理分析合集_第54张图片

thinkphp5 漏洞原理分析合集_第55张图片

因为执行的是get方法,跟进get()方法,此方法为读取配置文件,所以我们构造的参数进入get()中就会控制读取内容,后面返回self::$config[$range][$name[0]][$name[1]]。

thinkphp5 漏洞原理分析合集_第56张图片

也就是::config下的_sys_下的database下的username,也就是返回了数据库的用户名,我们构造的database.username,也可以换成database下的任意一个元素,database.password也就是查看数据库密码。

thinkphp5 漏洞原理分析合集_第57张图片

4.2 payload

http://127.0.0.1/tp5.0/public/index.php?s=index/thinkconfig/get&name=database.username

 

综合利用工具地址:

https://github.com/iceberg-N/thinkphp5.x_Scan

你可能感兴趣的:(安全知识,web安全,网络安全,安全,php)