首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

ThinkPHP request函数远程代码执行

0x01 概述

2019年1月11日爆了一个thinkphp 5.0.*远程rce的漏洞,跟进学习一下。

0x02 漏洞分析

根据ThinkPHP的补丁发现,修复部分在library/think/Request.php文件中。

漏洞的修复点是method这个函数,我们可以逆这回去看看,哪里调用了这个函数,相关调用方法出现在library/think/Request.php:541-603isGetisPostisPutisDeleteisHeadisPatchisOptions中,也就是说实际上这个函数会在判断请求方式的时候进行调用。

从修复代码来看,漏洞触发点应该是下图中第8行-第9行这个部分。

第8行代码有个var_method常量,这个常量的定义在application/config.php文件中,var_method对应的值是_method

也就是说,如果我们通过POST方式传入 的情况下,代码会将xxx转换为大写并赋值给。然后第9行调用,也就是调用,这就说明了攻击者在这个地方首先调用的函数可控,其次传入的数据也可控。

根据已知payload,这里的_method=___construct,也就是说___construct函数也有问题,跟进一下___construct函数,函数位置在library/think/Request.php:135-148中。

对传入的$options数组进行遍历,然后第4行调用了property_exists进行判断,property_exists函数的作用是检查对象或类是否具有该属性,也就是说当$options的键名为该类属性时,则将该类同名的属性赋值为$options中该键的对应值。这里第8行代码中针对$this->filter进行了判断,如果不存在,让其等于Config::get(‘default_filter’)的结果,而default_filter定义在application/config.php:44中,其值默认为空。

而filter存放的是全局过滤规则。

所以核心关键在于method这个函数在POST方法下可控,所以这里需要全局搜索一下除了那些http请求类型定义以外,还有哪里调用了这个函数,在library/think/Request.php:634-661调用了这个函数。

我们看到如果$this->mergeParam为空的情况下,调用$this->method(true),而true === $method情况下调用的是server(‘REQUEST_METHOD’)

跟进server函数,函数实现在library/think/Request.php:862,这里的$name实际上就是REQUEST_METHOD

经过处理之后,最后会调用$this->input函数进行处理,跟进input函数,函数位置在library/think/Request.php:999,这里第10行代码调用getFilter函数获取过滤器。

跟进一下getFilter函数,这里的$filter=’’,而$default=null

所以这里的代码会运行到第6行,进行三元运算,也就是说最终$filter会被赋值给$this->filter,最后返回$filter

紧接着判断$data是否是数组,然后调用filterValue函数进行处理。

跟进filterValue函数,在第7行看到了一个熟悉的函数call_user_func,而$filter$value均可控。

也就是说最后我们需要找到自动触发调用param()函数的地方即可,而在原生thinkphp框架下,文件位置在library/think/App.php:126,也就是说原生框架的情况下,如果开启了debug模式,可以直接命令执行。

0x03 动态调试

payload如下所示:

在开启debug状态之后,在param下一个断点,,

跟进param函数。

跟进method函数。

跟进server函数,这里input的函数输入分别是

跟进input函数中getFilter函数,处理结果返回filter数组,其中filter[0]=system。

$data就是我们刚刚的$this->server,对应的值也就是whoami,而filter[0]=system

跟进filterValue函数,最后成功运行了。

0x04 扩展

由于正式系统的情况下,使用debug模式的很少,因此需要找一下不需要debug模式下触发点,而漏洞发现者的思路有点像之前ThinkPHP远程代码执行那个思路。

payload:

直接动态调试吧,漏洞调用链是这样的。

先看看app.php:139位置,其中var_pathino 默认值为s,也就是说我们通过s参数注册了一个\think\captcha\CaptchaController的路由,至于为什么会是这样,可以翻一翻我之前的Thinkphp-5-0远程代码执行漏洞,里面针对路由怎么调用进行了详细的说明。

而在vendor/topthink/think-captcha/src/helper.php文件中针对captcha这个功能进行了路由注册,所以才能够调用。

而这里的返回type为什么是method,需要考究一下。在app.php:116处的routeCheck函数处下一个断点,跟进routeCheck,在thinkphp/library/think/App.php:643处调用check函数,跟进check函数thinkphp/library/think/Route.php:857调用了method函数。而method函数之前我们说过存在变量覆盖的问题,通过覆盖之后使得$method=get,然后再取出self::$rules[\$method]的值给$rules

然后继续往下走thinkphp/library/think/Route.php:873,此时使得的值为captcha路由数组,就可以进一步调用到函数。

跟进一下此时传递进来的$route的值为\think\captcha\CaptchaController@index,经过处理之后routeCheck函数处理之后type=method

前面我们传入的typemethod,所以进入到app:exec()中,会选择method这个case进行逻辑处理,而这个case正好调用了param这个函数,那么后面的流程自然就和0x02部分一样了。

0x05 总结

目前来看,漏洞触发需要两个前置条件,一种情况下如果采用thinkphp原生框架,需要在debug模式下才能够触发。另一种情况是找到一些第三方组件,并且该组件注册了thinkphp的路由,因为这步操作的影响就是改变了上文提到的的值,而thinkphp自带的一些第三方组件下,好像也只有captcha这个组件,学习了。

和同事在讨论过程中,发现下面这个poc用来验证比较准确点。

Reference

ThinkPHP5 核心类 Request 远程代码漏洞分析

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190119G08VQL00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券