yii反序列化漏洞复现及利用

yii反序列化漏洞

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 版代表新一代框架,是未来几年中我们的主要开发版本。

Yii 2.0 还使用了 PHP 的最新特性, 例如命名空间 和Trait(特质)

漏洞描述

yii2.2.0.38之前的版本存在反序列化漏洞,程序在调用unserialize时,攻击者可以通过构造特定的恶意请求执行RCE,CVE编号是CVE-2020-15148.

2.0.38已修复该漏洞,官方的修复方法:

在yii\db\BatchQueryResult类增加了一个__wakeup()函数,函数内容为:当BatchQueryResult类被反序列化时直接报错,wakeup()方法在类被反序列化会自动调用,这样也就避免了反序列化的发生,避免了漏洞。

环境复现

本地使用phpstudy搭建,将Yii的demo下载下来:

https://github.com/yiisoft/yii2/releases/tag/2.0.37

image-20220405150922884

修改/config/web.php里的cookieValidationKey为任何值,不然会报错:
image-20220405151034659

进入目录,执行 php yii serve

image-20220405151201210

进入http:localhost:8080

image-20220405151252335

前置知识点

namespace:

PHP: 命名空间概述 - Manual

php中命名空间(namespace)的作用和使用_古语静水流深-优快云博客

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():是在对象上下文中调用不可访问的方法时触发

由于是反序列化利用链,我们需要一个入口点,在controllers目录下创建一个Controller:

controllers/TestController.php:

url:http:localhost:8080/?r=test/test&data=xxx

<?php

namespace app\controllers;

class TestController extends \yii\web\Controller
{
   
    public function actionTest($data)
    {
   
        return unserialize(base64_decode($data));
    }
  
}

漏洞分析

漏洞触发点在\yii-2.0.37\vendor\yiisoft\yii2\db\BatchQueryResult.php文件中

/**
     * Destructor.
     */
    public function __destruct()
    {
   
        // make sure cursor is closed
        $this->reset();
    }

    /**
     * Resets the batch query.
     * This method will clean up the existing batch query so that a new batch query can be performed.
     */
    public function reset()
    {
   
        if ($this->_dataReader !== null) {
   
            $this->_dataReader->close();
        }
        $this->_dataReader = null;
        $this->_batch = null;
        $this->_value = null;
        $this->_key = null;
    }

__destruct()跟进reset(),继续跟进close(),发现close()里没有利用点。但是这里的 _dataRender是可控的,可以触发__call方法进行利用。

当一个对象调用不可访问的close方法或者类中没有close方法,即可触发 __call。全局搜索一下 __call方法,在\vendor\fzaninotto\faker\src\Faker\Generator.php中找到了合适的方法:

 /**
     * @param string $method
     * @param array $attributes
     *
     * @return mixed
     */
    public function __call($method, $attributes)
    {
   
        return $this->format($method, $attributes);
    }

这里的$methodclose函数,$attributes为空,因为close函数形参为空。继续跟进format方法

public function format($formatter, $arguments = array())
    {
   
        return call_user_func_array($this->getFormatter($formatter), $arguments);
    }

发现了call_user_func_array()函数,看到了一丝希望。在这里解释一下call_user_func_array()函数:

call_user_func_array:调用回调函数,并把一个数组参数作为回调函数的参数

使用方法:

call_user_func_array(callable $callback, array $param_arr): mixed 
callback
    被调用的回调函数。
param_arr
    要被传入回调函数的数组,这个数组得是索引数组。

示例:

<?php
namespace Foobar;
class Foo {
   
    static public function test($name) {
   
        print "Hello {
     $name}!\n";
    }
}
// As of PHP 5.3.0
call_user_func_array(__NAMESPACE__ .'\Foo::test', array('Hannes'));
//输出:Hello Hannes

// As of PHP 5.3.0
call_user_func_array(array(__NAMESPACE__ .'\F
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值