Laravel Debug mode RCE(CVE-2021-3129)分析复现
2021-01-14 19:21:41 Author: xz.aliyun.com(查看原文) 阅读量:349 收藏

测试环境

  • OS: MAC OS
  • PHP: 7.3.18
  • Laravel:8.22.0

环境搭建

根据原文(https://www.ambionics.io/blog/laravel-debug-rce)的搭建方式,把服务开起来。

访问http://127.0.0.1:8000/

可以看到这时候Ignition(Laravel 6+默认错误页面生成器)给我们提供了一个solutions,让我们在配置文件中给Laravel配置一个加密key。
点击按钮后会发送一个请求

通过这个请求Ignition成功在配置文件中生成了一个key。

接着页面就可以正常访问了,环境也就搭建完了

漏洞分析

漏洞其实就是发生在上面提到的Ignition(<=2.5.1)中,Ignition默认提供了以下几个solutions。

通过这些solutions,开发者可以通过点击按钮的方式,快速修复一些错误。
本次漏洞就是其中的vendor/facade/ignition/src/Solutions/MakeViewVariableOptionalSolution.php过滤不严谨导致的。
首先我们到执行solution的控制器当中去,看看是如何调用到solution的

<?php

namespace Facade\Ignition\Http\Controllers;

use Facade\Ignition\Http\Requests\ExecuteSolutionRequest;
use Facade\IgnitionContracts\SolutionProviderRepository;
use Illuminate\Foundation\Validation\ValidatesRequests;

class ExecuteSolutionController
{
    use ValidatesRequests;

    public function __invoke(
        ExecuteSolutionRequest $request,
        SolutionProviderRepository $solutionProviderRepository
    ) {
        $solution = $request->getRunnableSolution();

        $solution->run($request->get('parameters', []));

        return response('');
    }
}

接着调用solution对象中的run()方法,并将可控的parameters参数传过去。通过这个点我们可以调用到MakeViewVariableOptionalSolution::run()

<?php

namespace Facade\Ignition\Solutions;

use Facade\IgnitionContracts\RunnableSolution;
use Illuminate\Support\Facades\Blade;

class MakeViewVariableOptionalSolution implements RunnableSolution
{
    ...
    public function run(array $parameters = [])
    {
        $output = $this->makeOptional($parameters);
        if ($output !== false) {
            file_put_contents($parameters['viewFile'], $output);
        }
    }

    public function makeOptional(array $parameters = [])
    {
        $originalContents = file_get_contents($parameters['viewFile']);

        $newContents = str_replace('$'.$parameters['variableName'], '$'.$parameters['variableName']." ?? ''", $originalContents);

        $originalTokens = token_get_all(Blade::compileString($originalContents));
        $newTokens = token_get_all(Blade::compileString($newContents));

        $expectedTokens = $this->generateExpectedTokens($originalTokens, $parameters['variableName']);

        if ($expectedTokens !== $newTokens) {
            return false;
        }

        return $newContents;

文章来源: http://xz.aliyun.com/t/9030
如有侵权请联系:admin#unsafe.sh