Yoga7xm's Blog

Thinkphp5 RCE漏洞分析

字数统计: 1.7k阅读时长: 6 min
2019/03/21 Share

0x00 前言

抽空分析一波

0x01 5.0.x漏洞分析

POC集合

  • extend(需要think-captcha
    • POC 1(覆盖get变量)
    • POC 2 (覆盖server变量)
1
2
3
/public/index.php?s=captcha

_method=__construct&method=get&filter[]=system&get[]=whoami


1
2
3
/public/index.php?s=captcha

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


  • 开启debug模式下POC(extend or core)
1
2
3
/public/index.php

_method=__construct&filter[]=system&server[REQUEST_METHOD]=whoami

环境

PHP5.5.38+Thinkphp5.0.22完整版+PHPStorm+Apache

POC 1 Of Analyse

程序从入口函数APP::run()开始,会实例化一个Request对象,自动调用构造方法初始化所有参数。

根据URL路由检测解析调度,将返回值赋给$dispatch,然后会判断debug状态选择逻辑,这里变量覆盖的调用链为 App::routeCheck() ==> Route::check() ==> Request::method(),这里跟进routeCheck()

进行路由检测,跟进check()

这里会先进入method()方法中,然后,跟进method()

这里动态调用类,将取得的$_POST['_method']值赋给$method可以调用Request类中任意方法,选择动态调用__construct()方法来达到变量覆盖。

这里的$this->filter$this->get$this->method经过foreach循环后,被POST传入的值重新覆盖了,紧接着回到routeCheck()中,完成路由规则的解析及缓存机制的处理,得到的$result

然后将该值赋予$this->dispatch,由于未开启debug模式,所以程序就进入了exec()中,继续跟进

会根据传入的$dispath['type']来选择case的流程,URL中确定了s=captcha,所以该处Type为method;倘若按照默认的s=index,Type为module,会在module()中重新初始化$filter的值,将会覆盖已经覆盖的值。但是若开启了Debug却不用在意这些,因为执行的是另一条链,此为后话,继续跟进param()

获取参数,然后调用method(),此时$method === true,所以跳转至input(),继续跟进

此时$filter为null,然后进入getFilter(),跟进

当filter为null时,就会把Request::$this->filter赋给它,这里是system,然后回到input()中,此时data为POST,所以进入filterValue(),跟进

这里就直接调用了call_user_func(),传入了之前POST覆盖的$filter$value参数,而value是之前$this->param通过array_merge将当前请求参数和URL地址中的参数合并 ,正好$this->get被全局覆盖为whoami,在此处被调用,俩者均为可控所以造成了RCE,调用栈为

POC 2 Of Analyse

此处与前者的调用链几乎一致,只是在处理input数据时,利用了另一处入口,也就是method()

此时$method为True,所以将REQUEST_METHOD传入server()中,跟进

这里$name的值就是REQUEST_METHOD,而且传入input()的$this->server就是之前通过构造方法变量覆盖的server变量,所以接下来在进入input()中的$data就变成我们可控的点,然后经过call_user_func(),成功RCE,调用栈为

关于Debug

application/config.php下存放着系统配置文件config.php,开启debug,然后进入前面处理判断debug状态的run()

开启了debug后,就直接进入param()中,就不用进入之前的exec()再跳转回param(),然后后面的处理逻辑流程与之前一致,取得filter和server,然后call_user_function()

此外在之前的routeCheck()中,没有进入paserUrl()中调度并且路由注册,直接Type为module,所以并不需要s=xxxx控制器,也就是核心版也适用

至此,5.0.x的全部分析完成了,完整利用链如下(盗绿盟的图:sweat_smile:):

5.0.x补丁

传送门

$method()进行了校验,只允许为[‘GET’, ‘POST’, ‘DELETE’, ‘PUT’, ‘PATCH’] 五种方式(默认为POST),不再允许调用任意类方法

可利用版本问题

通过composer create-project topthink/think然后修改composer.json中TP的版本,即可下载任意版本的TP,然后批量Fuzz该版本的POC,得出结论

  1. 没有利用captcha等组件可以利用的版本

覆盖get变量的POC

1
5.0.2~5.0.12
  1. 利用captcha组件可以利用的版本

覆盖get变量的POC

1
5.0.2~5.0.23

覆盖server变量的POC

1
5.0.22/23

0x02 5.1.x漏洞分析

POC

1
2
3
POST /

a=system&b=whoami&_method=filter


环境

PHP5.6.27+Thinkphp5.1.17(with think-install)+PHPStorm+Apache

  1. composer create-project topthink/think 5117

  2. 修改json中TP版本为5.1.17

  3. composer update

Tip:TP5.1.x需要PHP>=5.6

Analyse

TP5.1之后加载的流程开始出现了比较大变化,所以这里在App::run()击中断点。

进入routeCheck()函数,

这里调用了若干个check()函数,然后在RuleGroup::checke()中调用了漏洞方法method()

这里不像之前的$this->{$this->method}($_POST);去构造__construct()方法覆盖变量,而是直接地覆盖,将$this->filter覆盖为POST传入的数组,$this->method覆盖为FILTER,然后返回$method

回到check()中,进入getMethodRules()进行路由分发

这里将$rules['filter']$rules[*]进行合并,但是数组中并没有filter的键名,所以这里会报错,所以这里必须要在入口函数处加error_reporting(0),逻辑才能继续,否则直接跳转至error中

这个位置必须得在ruqire之后才有效,这个条件正是这个POC鸡肋之处。然后回到run()中,跟进param()

这里又调用了method(),返回的$method为POST,所以进入case 'POST'流程,跟进post()

直接返回POST的数组,然后回到param()中,将参数合并为$this->param,然后传至input()中,跟进

这里跟之前版本的类似,先是调用了getFilter()$filter取出,然后在array_walk_recursive()中对$param递归调用回调函数filterValue(),而且传入的$data$filter都是POST的三个值,继续跟进

foreach中遍历$filter,然后传入至call_user_func(),成功RCE!!!

至此,5.1.x的全部分析完成了,完整利用链如下(又盗绿盟的图😅):

5.1.x补丁

传送门

对$method()进行了校验,只允许为[‘GET’, ‘POST’, ‘DELETE’, ‘PUT’, ‘PATCH’] 五种方式(默认为POST),不再允许再调用任意类方法或者直接覆盖原有变量了

可利用版本问题

同样通过composer下载全部版本的TP,然后批量在入口文件中加入error_reporting(0);FUZZ测试POC可以得出可利用版本:

5.1.0
5.1.14
5.1.16
5.1.17
5.1.19
5.1.20
5.1.21
5.1.22
5.1.23
5.1.24
5.1.25
5.1.26
5.1.27
5.1.28
5.1.29
5.1.31
5.1.32

此外,更为鸡肋的一点就是在/vendor/topthink/中不能存在除think-installer外的其他依赖…..

0x03 结尾

这个漏洞处的Request::method()方法,导致影响版本数量之多,这个着实比较令人头大,而且利用条件也比较鸡肋(运气好),但是利用链非常的巧妙,膜一波Orz

CATALOG
  1. 1. 0x00 前言
  2. 2. 0x01 5.0.x漏洞分析
    1. 2.1. POC集合
    2. 2.2. 环境
    3. 2.3. POC 1 Of Analyse
    4. 2.4. POC 2 Of Analyse
    5. 2.5. 关于Debug
    6. 2.6. 5.0.x补丁
    7. 2.7. 可利用版本问题
  3. 3. 0x02 5.1.x漏洞分析
    1. 3.1. POC
    2. 3.2. 环境
    3. 3.3. Analyse
    4. 3.4. 5.1.x补丁
    5. 3.5. 可利用版本问题
  4. 4. 0x03 结尾