ThinkPHP 5.0.0~5.0.23 RCE 漏洞分析

最近TP被爆出三个RCE,想跟着大佬们写的文章,把这几个洞分析一下,这里是 5.0.0~5.0.23 的RCE。官方补丁地址:https://github.com/top-think/framework/commit/4a4b5e64fa4c46f851b4004005bff5f3196de003

exp:

http://127.0.0.1/index.php?s=captcha

post_poc1:_method=__construct&filter[]=system&method=get&get[]=whoami
post_poc2:_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami

任意文件包含

_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=/etc/passwd

未开启Debug
图片.png

图片.png

图片.png

漏洞触发流程

首先进入入口点App.php中的run方法,实例化了一个$request对象传给了routeCheck方法。
图片.png
跟进Route::check
图片.png
$method$request->method()获得,$rules会根据$method的不同而获得不同的路由规则
图片.png
跟进$request->method(),漏洞点在这里,通过外部传入Config::get('var_method')可以造成该类的任意方法调用。
图片.png
在tp的默认中配置中设置了表单请求类型伪装变量如下,POST一个_method参数即可进入判断。
图片.png
Request类的构造方法中,$options可控,因此我们可以覆盖该类的任意属性。其中$this->filter保存着全局过滤规则。
图片.png
我们请求的路由是?s=captcha,它对应的注册规则为\think\Route::get
图片.png
因此我们需要让$this->method返回值为get,所以payload中有个method=get,然后才会取出self::$rules[$method]的值给$rules。
路由检测后接着会执行 self::exec,并进入method分支
图片.png

跟进Request::instance()->param()$this->param通过array_merge将当前请求参数和URL地址中的参数合并。
图片.png

跟进$this->input,该方法用于对请求中的数据即接收到的参数进行过滤,而过滤器通过$this->getFilter获得。
图片.png
图片.png
$this->filtersystem,回到input,因为data为数组,因此可以进入if条件调用array_walk_recursive($data, [$this, 'filterValue'], $filter),对$data中的每一个值调用filterValue函数。
图片.png
即间接调用call_user_func,且两个参数可控,造成RCE。
图片.png

poc2

回到param方法,跟进method方法,此时参数为True。
图片.png
因此会进入$this->server
图片.png
这里也有一个input
图片.png
且此时input中的data对应于server$this->servername的值为REQUEST_METHOD。因此传入server[REQUEST_METHOD]=whoami,因为此时data不是数组,所以会直接进入filterValue
图片.png
结合之前的__construct覆盖$filter属性,同样可以造成RCE。
图片.png

补丁分析

图片.png
对表单请求类型伪装变量添加了白名单,防止任意属性覆盖。

后记

这个洞的攻击链很强,Orz!!。

Referer

ThinkPHP 5.0.0~5.0.23 RCE 漏洞分析
ThinkPHP5 核心类 Request 远程代码漏洞分析