ThinkPHP5.X远程代码执行漏洞分析

1、漏洞介绍

ThinkPHP 5.x 版本中没有对路由中的控制器进行严格过滤,在存在adminindex模块、没有开启强制路由的条件下(默认不开启),导致可以注入恶意代码利用反射类调用命名空间其他任意内置类,从而被 GetShell。

2、影响版本

ThinkPHP 5.0-5.0.22

ThinkPHP 5.1.0-5.1.30

3、下载地址

本文用ThinkPHP 5.0.22完整版进行分析,下载地址:http://www.thinkphp.cn/down/1260.html

4、漏洞触发流程

这里先从ThinkPHP入口点开始分析,在/thinkphp/library/think/App.php文件中

run函数在第一行会请求request类,并给request赋值,然后根据请求的URL调用routeCheck($request, $config)

在第643行可以看到触发Route::check会根据路由自定义返回不同的URL调度

在这里我们可以控制$request->method()返回过来的值,赋值给$method,然后取出self::$rules[$method]赋值给$rules

所以说当攻击者控制$method的值为get的时候,$rules的值就是这条路由的规则,回到check函数,此时传入的URL就是$item的值,进一步调用到self::parseRule函数

最后会将$result层层返回到run函数中,并赋值给了$dispatch

而$dispatch带入到self::exec`函数中

最后可以看到在第460行,调用Request类的param方法。

5、漏洞分析

/thinkphp/library/think/Request.php

第525行,这里引用了一个外部可控的数据,其中var_method这个常量在application/config.php中对应的值是_method

也就是说我们POST传入的_method的值会赋值给$this->method,在method方法中第526行,可以看到我们_method赋的值是可控的,其次传入的数据也是可控的。

根据网上爆出的payload,可以看到_method=__construct,那么就是引用的__construct这一函数

这个函数会对传入的$options进行数组遍历,将Request对象的成员属性进行覆盖

$this->filter也保留着全局过滤的规则,而在/thinkphp/library/think/App.php中,由于$dispatch值为method,所以会进入第到468行这一分支

跟入Request::instance()->param()这一方法

在第637行,当$this->mergeParam为空时,$this->method(true)

而在method方法中如果true === $method,就会调用原始请求server('REQUEST_METHOD'),跟进server这一函数

看到最后会经过$this->input()处理,继续跟进input函数

在第1023行,可以看到使用解析过滤器$this->getFilter,跟进这一函数

在第1058行,$filter会直接赋值$this->filter,并将结果返回

查看filterValue函数

在第1083行,可以看到call_user_func函数,其中$filter$value都是可控的,那么我们直接命令执行

6、漏洞复现

1
2
3
http://localhost:8081/public/index.php?s=captcha
POST:
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ifconfig

1
2
3
http://localhost:8081/public/index.php?s=captcha
POST:
_method=__construct&filter[]=system&method=get&get[]=whoami

1
http://localhost:8081/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()

7、漏洞修复

增加对$method的白名单,不允许任意调用函数

8、参考链接