1、漏洞介绍
ThinkPHP 5.x 版本中没有对路由中的控制器进行严格过滤,在存在admin
、index
模块、没有开启强制路由的条件下(默认不开启),导致可以注入恶意代码利用反射类调用命名空间其他任意内置类,从而被 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 | http://localhost:8081/public/index.php?s=captcha |
1 | http://localhost:8081/public/index.php?s=captcha |
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的白名单,不允许任意调用函数