前言
之前接触yii2,接下来就遇到laravel5.7的反序列化了,跟着大师傅们的文章复现了一下laravel5.7的反序列化链,学习了一波。
CVE编号是CVE-2019-9081:
The Illuminate component of Laravel Framework 5.7.x has a deserialization vulnerability that can lead to remote code execution if the content is controllable, related to the __destruct method of the PendingCommand class in PendingCommand.php.
去github上下载源码:
laravel5.7,下载下来的可能回没有vendor目录,需要在根目录执行composer install就可以了。
然后就是构造一个反序列化的利用点了,在routes/web.php里面加一条路由:
Route::get('/unserialize',"UnserializeController@uns");
然后在App\Http\Controllers下面写一个控制器:
<?php
namespace App\Http\Controllers;
class UnserializeController extends Controller
{
public function uns(){
if(isset($_GET['c'])){
unserialize($_GET['c']);
}else{
highlight_file(__FILE__);
}
return "uns";
}
}
准备工作就做完了,开始分析反序列化链。
反序列化链分析
有一说一这条链真要完完全全进行分析的话,需要一定的开发水平,因为像我这样第一次接触laravel的0开发小白+代码审计小白,利用上注释,能清晰的理解这条链上三分之一的代码就很难得了,所以这条链的审计给我的体会就是学会打断点,忽略掉无用代码(我看不懂的就是无用的,笑),只要一路下去能顺利执行,就不要管中间那些代码是干啥的。
和laravel5.6相比,laravel5.7多了PendingCommand.php这个文件:

该类的作用是命令执行,并获取输出内容。
看一下这个新增的类,发现有一个__destruct()。经过了之前yii2的审计,现在看到__destruct()就很兴奋:

$this->hasExecuted默认是false的,所以可以直接进入run方法:
/**
* Execute the command.
*
* @return int
*/
public function run()
{
$this->hasExecuted = true;
$this->mockConsoleOutput();
try {
$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);
} catch (NoMatchingExpectationException $e) {
if ($e->getMethodName() === 'askQuestion') {
$this->test->fail('Unexpected question "'.$e->getActualArguments()[0]->getQuestion().'" was asked.');
}
throw $e;
}
if ($this->expectedExitCode !== null) {
$this->test->assertEquals(
$this->expectedExitCode, $exitCode,
"Expected status code {
$this->expectedExitCode} but received {
$exitCode}."
);
}
return $exitCode;
}
文档注释上写着Execute the command,我差点都以为这是开发留的后门了。。。
不过我们要明确一点,我们最终的目的就是这里:
$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);
所以跟进到$this->mockConsoleOutput();:
/**
* Mock the application's console output.
*
* @return void
*/
protected function mockConsoleOutput()
{
$mock = Mockery::mock(OutputStyle::class.'[askQuestion]', [
(new ArrayInput($this->parameters)), $this->createABufferedOutputMock(),
]);
foreach ($this->test->expectedQuestions as $i => $question) {
$mock->shouldReceive('askQuestion')
->once()
->ordered()
->with(Mockery::on(function ($argument) use ($question) {
return $argument->getQuestion() == $question[0];
}))
->andReturnUsing(function () use ($question, $i) {
unset($this->test->expectedQuestions[$i]);
return $question[1];
});
}
$this->app->bind(OutputStyle::class, function () use ($mock) {
return $mock;
});
}
一堆我看不懂的代码,不过问题不大,只要可以正常执行到命令执行的call函数,就问题不大,写个POC试试:
<?php
namespace Illuminate

本文详细介绍了复现Laravel5.7框架中一个反序列化漏洞的过程,涉及PHP的反序列化、魔术方法、laravel命令执行等技术。通过构造特定的反序列化链,最终实现了远程代码执行。文章适合有一定开发基础并关注代码审计和安全研究的读者。
最低0.47元/天 解锁文章
4696





