yii漏洞复现
简介
Yii是一个适用于开发Web2.0应用程序的高性能PHP框架
Yii是一个通用的WEB编程框架,即可以用于开发各种用PHP构建的Web应用。因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用,如门户网站、社区、内容管理系统(CMS)、电子商务项目和RESTful Web服务等。
Yii当前有两个主要版本:1.1和2.0。1.1版是上代的老版本,现在处于维护状态。2.0版是一个完全重写的版本,采用了最新的技术和协议,包括依赖包管理器Composer、PHP代码规范PSR、命名空间、Traits(特质)等等。2.0版代表新一代框架,是未来几年中我们的主要开发版本。
Yii2.0还使用了PHP的最新特性,例如命名空间和Trait(特质)
漏洞描述
Yii2 2.0.38之前的版本存在反序列化漏洞,程序在调用unserialize时,攻击者可通过构造特定的恶意请求执行任意命令。CVE编号是CVE-2020-15148
2.0.38已修复,官方给yii\db\BatchQueryResult类加了一个__wakeup()函数,__wakeup方法在类被反序列化时会自动被调用,而这里这么写,目的就是在当BatchQueryResult类被反序列化时就直接报错,避免反序列化的发生,也就避免了漏洞。
环境复现
下载地址:https://github.com/yiisoft/yii2/releases/tag/2.0.37
下载yii-basic-app-2.0.37.tgz
本地使用phpstudy进行环境搭建
修改/config/web.php文件17行cookieValidationKey,值可以为任何,作为yii\web\Request::cookieValidationKey的加密值,不设置会报错
进入目录,执行php yii serve
进入http://localhost:8080/
知识点
Yii2.0 路由(Route)的实现原理 [ 2.0 版本 ]
所谓路由是指URL中用于标识用于处理用户请求的module,controller,action的部分,一般情况下由r查询参数来指定。
如http://www.digpage.com/index.php?r=post/view&id=100
,表示这个请求将由PostController的actionView来处理。(主要首字母要大写)
__construct(): 当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。
__destruct(): 当对象被销毁时会自动调用。
__call():是在对象上下文中调用不可访问的方法时触发。
分析:
使用的poc:
1 |
|
先从poc代码开始入手:
其它师傅的图图
第一步:new了一个yii\db\BatchQueryResult()类,那么就会调用这个类的__construct()方法
第二步:这个类的__construct()方法会new一个Faker\Generator()类,就又会调用Faker\Generator()类的__construct()方法,并把这个对象赋值给$_dataReader变量。
第三步:Faker\Generator()类的__construct()方法会new一个yii\rest\CreateAction()类,然后又调用yii\rest\CreateAction()类的__construct()方法。并调用了run方法(这里用了 call_user_func_array),然后赋值给$this->formatters[‘close’]。
第四步:反序列化时,类中变量的值我们是可控的;那么就可以通过yii\rest\CreateAction()类的__construct()方法中修改这些变量,将这些值改成命令,成功RCE。
思路有了,看看源文件中的这些类:yii\db\BatchQueryResult():
文件在:
1 |
|
在poc中
1 |
|
这句话执行完之后,会先调用yii\db\BatchQueryResult()类的__destruct()方法,这个方法会执行reset方法,因为前面我们将$this->dataReader设置为了Faker\Generator()对象
1 |
|
所以就会进入if语句,执行Gnerator的close方法,因为没有这个方法所以会调用他的__call()方法
Gnerator类
文件在:
1 |
|
因为close是无参方法,所以__call中的$method是close,attributes为空,然后传参给format方法
继续跟进format方法
这里因为传参,所以$formatter=’close’,$arguments=空
format方法中使用了call_user_func_array(),使用的方法为getFormatter(‘close’)的返回值,参数在$arguments=空
继续跟进getFormatter(‘close’)方法,这个方法中if语句中的$formatters[‘close’],我们发现:
$formatters[‘close’]是可控的,也就是说getFormatter方法的返回值是可控的,也就是说call_user_func_array这个函数的第一个参数可控,第二个参数为空;所以相当于我们现在能干两件事
调用yii2中任意的一个无参方法
调用原生php的类似于phpinfo()这样的无参方法,但是第二种肯定不能RCE,因此还要在yii2中已有的无参方法中进行挖掘
搜索含有call_user_function的无参函数
最后找到rest/CreateAction.php以及rest/IndexAction.php
这里分析CreatAction.php
那么poc中的下一步代入了一个无参数的方法去RCE,在前面的poc中说了formatter被赋值为
1 |
|
CreateAction类
文件在
1 |
|
CreatAction()方法中的$this->checkAccess,$this->id我们都是可控的,那么就可以执行RCE了
复现
复现的第一步,先自己添加一个反序列化的入口,在controllers目录下新建一个存在反序列化点的TestController.php
然后使用poc输出需要的字符串
1 |
|
get传参(使用的知识是路由)
1 |
|
成功RCE